41

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.

GreenScape
  • 7,191
  • 2
  • 34
  • 64
  • 2
    On en.cppreference.com it says for [default template arguments](http://en.cppreference.com/w/cpp/language/template_parameters#Default_template_arguments) »Default template arguments that appear in the declarations and the definition are merged similarly to default function arguments«. For [default function arguments](http://en.cppreference.com/w/cpp/language/default_arguments) I found »... indicated by using the following syntax for a parameter in the parameter-list of a function declaration«. If I interpret this correctly, then you *have* to have the default argument in the declaration. – Henri Menke May 16 '17 at 05:58
  • @HenriMenke, it works for functions in **gcc**: https://godbolt.org/g/kXNbYi . It seems that **gcc** has double standards for functions and templates... – GreenScape May 16 '17 at 06:12
  • @HenriMenke See an example of `template<...>class A` at 14.1/10. It works as is in g++, but not if you replace a class with a function. – n. m. could be an AI May 16 '17 at 06:37
  • 3
    [This is a much simpler example](https://godbolt.org/g/iH9Pdt), it's is lifted straight from the standard and converted from class to function template. – n. m. could be an AI May 16 '17 at 06:47
  • Is it really characteristic of your problem that the function definition (whereupon the default template parameter resides) is in scope at the point where the function is called? In that case, what is the problematic prior declaration actually doing for you? – John Bollinger May 16 '17 at 14:38

1 Answers1

34

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
GreenScape
  • 7,191
  • 2
  • 34
  • 64
  • 1
    Default template arguments for function templates is relatively new (C++11). Not that surprising that the GCC folks missed it. – T.C. May 16 '17 at 20:04