Which values can be assigned to a `constexpr` reference?

拈花ヽ惹草 提交于 2019-12-13 19:21:37

问题


Original Question

I would like to use static member variables in order to pass information via a type template parameter into templated classes. These variables should not be set in a header file that is included in all translation units so that I can change them without recompiling most of the object files. Moreover, it would be nice to have a handy alias for the variable that does not require additional space. I thought that constexpr read-only references like

static constexpr const int& alias = T::static_variable_name;

could serve as such an alias, but was not sure if that is valid. One restriction for constexpr reads

  • the constructor parameters or the value to be assigned must contain only literal values, constexpr variables and functions.

So I tried it using g++ -std=c++11 and got kind of inconsistent behavior for my taste. The code is included at the end of this question. In most of the cases, the code compiles and runs fine, but when using h2g2_oracle::answer directly in the non-specialized class template (see comment "ERROR; WHY?"), compilation fails with the message

src/main.cpp:18:57: error: the value of ‘h2g2_oracle::_answer’ is not usable
in a constant expression
      static constexpr const int& answer = h2g2_oracle::answer; // ERROR; WHY?
src/main.cpp:11:9: note: ‘int h2g2_oracle::_answer’ is not const
      int h2g2_oracle::_answer = 42;

Why do most of the constexpr references work as an alias and this single one does not?

#include <iostream>

// a specific kind of oracle
class h2g2_oracle {
  protected:
    static int _answer;

  public:
    static constexpr const int& answer = _answer; // public alias for reading
};
int h2g2_oracle::_answer = 42;

// some class template using a specific kind of oracle
template<typename oracle>
struct forecast {
  // try to define an own alias
  static constexpr const int& answer = oracle::answer; // works
  //static constexpr const int& answer = h2g2_oracle::answer; // ERROR; WHY?
};

// specialized version for the h2g2_oracle
template<>
struct forecast<h2g2_oracle> {
  // also define the own alias
  static constexpr const int& answer = h2g2_oracle::answer; // works
};

int main() {
  static constexpr const int& answer = h2g2_oracle::answer; // works
  std::cout << answer << std::endl;

  std::cout << forecast<h2g2_oracle>::answer << std::endl;
  return 0;
}

Addressing the comments

@Ben Voigt concerning the gain of constexpr: Yes, dyp is right with the assumption that I like the constexpr version for its initialization inside the body. Long comment: I was very sure that an initialization in a separate translation unit would force the program to use some memory location where the address of the original value is stored. Therefor, I thought constexpr might be useful to always have the static member initialization in the header, i.e., for templates (forecast<...>::answer) and non-templates (h2g2_oracle::answer). I know that I could change the non-template class to include a dummy template parameter so that initialization could be done in the header as well, but still the solution with constexpr initially felt easier to me.

@dyp concerning a possible g++ bug: Ah, I did not expect to find one with such a "simple" test. Now that I see the related questions, that seems like a good explanation to me. Any practicable way for me to confirm/report/help? If you are quite confident with that, I could accept that as an answer at least.

@dyp concerning definition before using: Yes, I checked that g++ is fine with using oracle::answer in the non-specialized forecast even if the definition of h2g2_oracle comes after the definition of forecast. However, h2g2_oracle has to be complete when forecast<h2g2_oracle>::answer is used in the main. This looks reasonable to me. Additional thought: What I am more fascinated about is the way how static members can be initialized as a reference to another static member (possibly in a different translation unit).

@dyp concerning

Interestingly, I'm not sure if you need a definition for h2g2::answer before using it in forecast; it should be odr-used (which I find strange).

I think I got your point now. This is also what I find fascinating: In fact, the int h2g2_oracle::_answer = 42; can even be moved to a different translation unit and it still works. Somehow, the linker (im simple words) manages to "insert" the correct memory address of h2g2_oracle::_answer where it is needed.


回答1:


Simple workaround which is inline and certainly requires no storage:

template<typename oracle>
struct forecast
{
  static const int& answer() { return h2g2_oracle::answer; }
};

It is also C++98-compatible.



来源:https://stackoverflow.com/questions/26121939/which-values-can-be-assigned-to-a-constexpr-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!