Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why class template instantiation fails for unused member template function

Tags:

c++

templates

Whats wrong with this:

#include <type_traits>

struct A;

template<typename T>
struct B
{
    template<typename=std::enable_if<std::is_copy_constructible<T>::value>>
    void f1() {}
};

template<typename T>
struct C {};


// Type your code here, or load an example.
int main() {
    // Following fails
    B<A> b;
    // Could use this:
    // b.f1<C>();

    // This complies
    C<A> c;

    return 0;
}

/* This to be in or not doesn't make a difference
struct A
{};
*/

I tried this here: https://godbolt.org/z/NkL44s with different compilers:

  • x86-64 gcc 9.2: compiles
  • x86-64 gcc (trunk): fails
  • x86-64 clang 6.0.0: compiles
  • x86-64 clang 7.0.0 and later: fails
  • x64 msvc v19.22: compiles
  • x64 msvc v19.23 (tested internally): fails

So why do more recent compilers reject this? When instantiating B<A> it is not clear in which form f1 will be used or if it will be used at all. So why the compiler complains about it? Shouldn't the f1 member template function be checked only if it is really used?


Edit:
As mentioned in comments, I made an unintented mistake in above code: std::enable_if should have been std::enable_if_t, as in this corrected playground: https://godbolt.org/z/cyuB3d

This changes the picture of compilers passing this code without error:

  • gcc: fails
  • clang: fails
  • x64 msvc v19.22: compiles
  • x64 msvc v19.23 (tested internally): fails

However, the question remains: Why does a defaulted template parameter of a function that is never used lead to compilation failure?

like image 907
Jojakim Stahl Avatar asked Oct 10 '19 10:10

Jojakim Stahl


People also ask

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.

How the instantiation of function template happens?

When a function template is first called for each type, the compiler creates an instantiation. Each instantiation is a version of the templated function specialized for the type. This instantiation will be called every time the function is used for the type.

Can a member function be a template?

Member functions can be function templates in several contexts. All functions of class templates are generic but aren't referred to as member templates or member function templates. If these member functions take their own template arguments, they're considered to be member function templates.

What is the instantiation of the class template?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation.


2 Answers

The reason is that std::is_constructible requires a complete type: (Table 42)

Template

template <class T>
struct is_­copy_­constructible;

Preconditions

T shall be a complete type, cv void, or an array of unknown bound.

Failing to meet a library "shall" requirement results in undefined behavior.

like image 81
L. F. Avatar answered Sep 30 '22 01:09

L. F.


From cppreference on is_copy_constructible<T>:

T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound. Otherwise, the behavior is undefined.

So it seems like you have just plain UB in older compiler versions while the newer ones are nice enough to tell you that A has to be a complete type.

Note that in the presence of UB, compliers are not reuqired to issue an error, but they may do it, which is a nice thing.

like image 22
463035818_is_not_a_number Avatar answered Sep 29 '22 23:09

463035818_is_not_a_number