ODR of template class with static constexpr member

本小妞迷上赌 提交于 2019-12-08 05:38:11

问题


I know, there are many answered question about linkage of a static (constexpr) members.

But I wonder, why using a template class out-of-line definition works in a header file but not for a specialized class.

a) This works without linker error:

template<typename, typename>
struct Foobar;

template<typename T>
struct Foobar<int, T> {
  static constexpr std::array<int, 1> a = {{1}};
};

template<typename T>
constexpr std::array<int, 1> Foobar<int, T>::a;

// foo.cpp
std::cout << Foobar<int, int>::a[0] << "\n";

// bar.cpp
std::cout << Foobar<int, int>::a[0] << "\n";

The objdump of:

foo.o: 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE

bar.o: 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE

Linked file: 0000000000475a30 w O .rodata 0000000000000004 _Z6FoobarIiiE1aE

b) This does not (multiple definition):

template<typename>
struct Foobar;

template<>
struct Foobar<int> {
  static constexpr std::array<int, 1> a = {{1}};
};
constexpr std::array<int, 1> Foobar<int>::a;

// foo.cpp
std::cout << Foobar<int>::a[0] << "\n";

// bar.cpp
std::cout << Foobar<int>::a[0] << "\n";

The objdump of:

foo.o 0000000000000100 g O .rodata 0000000000000004 _Z6FoobarIiE1aE

bar.o: 0000000000000420 g O .rodata 0000000000000004 _Z6FoobarIiE1aE

What we see, the out-of-line definition has different addresses inside the object files (example b)).

My question to you:

  1. Is it save to use the template trick? What are the disadvantage?
  2. Would it be useful to relax the definition of odr for such cases like b in the future?

Thank you in advance!


回答1:


See [basic.def.odr]/6:

There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. ...

The effect of this rule is that every template-declaration behaves as though it is inline. (But it does not extend to explicit-instantiation and explicit-specialization declarations.)

In the first snippet, you have

template<typename T>
constexpr std::array<int, 1> Foobar<int, T>::a;

which is a template-declaration and therefore is allowed to be multiply defined. In the second snippet, you have

constexpr std::array<int, 1> Foobar<int>::a;

which is not a template-declaration: the definition itself is not templated, even though the thing being defined happens to be a specialization of a template.

My question to you:

  1. Is it save to use the template trick? What are the disadvantage?

There is no "trick" here. If you want to define the member for all Foo<T>, then you have no choice but to put the definition in the header file. If you want to define the member for one specific Foo<T> such as Foo<int>, then you must not put the definition in the header file (until C++17, which introduces inline variables.) There is no trick because what you are supposed to do depends on your specific goal.

(Your second question was answered in the comment section.)



来源:https://stackoverflow.com/questions/42220688/odr-of-template-class-with-static-constexpr-member

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