12

Why does emplace_back take a reference of the member that requires a definition? What is the difference between emplace_back(integer literal) and emplace_back(static constexpr integer member)?

If I switch to C++17, it compiles fine. I found that in C++17 static constexpr data members are implicitly inlined. Does it mean the compiler implicitly creates a definition for them?

Example code:

class base {
    int n;
public:
    base(int n):n(n) {}
};

struct base_trait {
    static constexpr int n = 1;
};

int main(void) {
    vector<base> v;
    v.emplace_back(1);  // ok
    v.emplace_back(base_trait::n);  // link error with -std=c++14, ok with -std=c++17
    return 0;
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
mljli
  • 593
  • 5
  • 23

1 Answers1

9

As you said, emplace_back takes arguments by reference, so passing base_trait::n causes it to be odr-used.

an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it;

Before C++17, that means the definiton of base_trait::n is required here. But since C++17 the behavior changed, for constexpr static data member the out-of-class definition is not needed again.

If a const non-inline (since C++17) static data member or a constexpr static data member (since C++11) is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. This definition is deprecated for constexpr data members (since C++17).

A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition. (since C++17)

Community
  • 1
  • 1
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • So rvalue reference needs a definition as well as lvalue reference? – mljli May 27 '18 at 06:09
  • 2
    @mljli It causes the argument to be odr-used; it's same as lvalue-reference. Before C++17 that means the definition is required. Ater C++17 the definiton of static constexpr data member is not required again, so your code works fine with C++17. – songyuanyao May 27 '18 at 06:12
  • @mljli Why do you expect rvalue ref to be different? When used, both are just normal references (lvalues). – curiousguy Jul 10 '18 at 09:10
  • @curiousguy I thought const lvalue reference and rvalue reference don't require a definition. And static const member can just be used as literal, i.e., without storage/address. – mljli Jul 11 '18 at 13:53
  • @mljli Can you post an simple example of a ref that you believe does/should/could *not* require a definition? – curiousguy Jul 11 '18 at 23:01
  • @curiousguy I thought that because I forgot that compiler constructs temporary variables when passing literals. Literals are not variables so they don't have a definition. Right? – mljli Jul 12 '18 at 02:41
  • @mljli Literals are really pure rvalues. They don't have an address or a lifetime. When referencing binding is required, the compiler will create a **temporary object** (it won't create a "variable"). – curiousguy Jul 12 '18 at 03:15