Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I inline-define a non-templated friend within a templated class?

MCVE's speak louder than words:

// int bar();
template <bool B> class Foo {
    friend int ::bar() { return 123; }
};

int main()
{
    Foo<false> f1;
    Foo<true> f2;
}

with GCC 6 and --std=c++14, this gives me:

a.cpp: In instantiation of ‘class Foo<true>’:
a.cpp:9:12:   required from here
a.cpp:3:13: error: redefinition of ‘int bar()’
  friend int ::bar() { return 123; }
             ^~
a.cpp:3:13: note: ‘int bar()’ previously defined here

Now, I'm not sure what the standard says; but I know that the compiler knows that the friend is not templated on B, nor does its definition use B. So why can't it apply the "oh, all inline copies of the same definition of a function are the same" rule?

like image 697
einpoklum Avatar asked Sep 24 '17 23:09

einpoklum


People also ask

Can a non templated class have a templated member function?

A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.

Are templated functions inline?

Yes, they are inline . Most implementations can't do separate instantiation of template functions, so you have to include them in every place you intend to use them.

How do I force a template instantiation?

To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>(float original); Template arguments may be omitted when the compiler can infer them.

Can a template class inherit from a non-template class?

Deriving from a non-template base classIt is quite possible to have a template class inherit from a 'normal' class. This mechanism is recommended if your template class has a lot of non-template attributes and operations. Instead of putting them in the template class, put them into a non-template base class.


1 Answers

Now, I'm not sure what the standard says;

This case has in fact been clarified with an example in the upcoming C++17

[temp.inst]/2 The implicit instantiation of a class template specialization ... [snip] ... for the purpose of determining whether an instantiated redeclaration of a member is valid according to 3.2 [basic.def.odr] and 9.2 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [ Example:

... [snip (another example)] ...

template<typename T> struct Friendly {
  template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff; // ill-formed: produces second definition of f(U)

— end example  ]

Admittedly as you point out, the example of the standard does produce a different definition for each instantiation, but that is not necessary for the example to be ill-formed according to that rule.

So why can't it apply the "oh, all inline copies of the same definition of a function are the same" rule?

This question seems to apply to the a much simpler situation as well:

inline void foo(){}
inline void foo(){}

Surely a compiler can see that the definitions are identical, just as much as a compiler can see that the definition of your ::bar depends not on the template argument of Foo.

Yet, odr says that the re-definition is ill-formed. This is true for definitions outside a class template, as well as definitions that are caused by instantiation of a class template.


Perhaps odr could be relaxed for the case that you demonstrate, but that would require complicating the standard with a special case rule, and complicate the compilers that then would have to analyse whether template arguments are used within the definition, so such relaxation certainly isn't without compromise.

like image 93
eerorika Avatar answered Dec 07 '22 04:12

eerorika