Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

All versions of GCC struggle with a template that has the default type in a definition

I wasted countless hours to pinpoint an issue with gcc. I wanted to test our code base with another compiler to look for more warnings that Clang might have missed. I was shocked that practically half of the project stopped to compile due to failure of template argument deduction. Here I've tried to dumb my case down to the simplest piece of code.

#include <type_traits>

struct Foo
{ };

// This is a template function declaration, where second template argument declared without a default
template <typename T, typename>
void foo(const Foo & foo, T t);

// This is a template function definition; second template argument now has a default declared
template <typename T, typename = typename std::enable_if<1>::type>
void foo(const Foo & foo, T t)
{
}

int main(int argc, char ** argv)
{
    foo(Foo{}, 1);
    return 0;
}

Ignore a 1 in the std::enable_if<1>. Obviously it's a constant value just to not complicate things when it does not matter.

This piece of code compiles[1] with clang (3.4 through 4.0), icc (16, 17), Visual C++ (19.00.23506). Basically, I couldn't find any other c++11 compiler that, except gcc (4.8 through 7.1), does not compile this piece of code.

The question is, who's right and who's wrong here? Does gcc behave according to the standard?

Obviously this is not a critical issue. I can easily move std::enable_if to the declaration. The only victim would be aesthetics. But it is nice to be able to hide an ugly 100 characters long std::enable_if piece of code, that is not immediately relevant for the user of the library function, in the implementation.


Live example on godbolt.org.

like image 870
GreenScape Avatar asked May 16 '17 05:05

GreenScape


1 Answers

What the standard says ([1] page 350):

The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way default function arguments are (8.3.6). [ Example:

template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;

— end example ]

So GCC is wrong here. It ignores default template arguments in declarations.

Not all declarations, only function template declarations. Class template declarations are okay:

#include <type_traits>

template <typename T, typename>
struct Foo;

template <typename T, typename = typename std::enable_if<1>::type>
struct Foo
{
    T t;
};

int main()
{
    Foo<int> foo;
    return 0;
}

Live example on godbolt.org


Probably it is due to the nature of how non-default arguments are deduced. In the function template they are deducted from function arguments. In the class template we have to specify them explicitly.

Anyway, I have created a bug report.

like image 119
GreenScape Avatar answered Oct 14 '22 18:10

GreenScape