问题
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:
- Is it save to use the template trick? What are the disadvantage?
- 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:
- 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