Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a c++11 variadic function template overload with a dependent type ambiguous?

The following code is a textbook example of a recursive variadic function overload. In both clang and GCC, it compiles cleanly, and main returns 36 (as expected):

template <typename T>
int add(T val)
{
    return val;
}

template <typename FirstTypeT, typename... RestT>
int add(FirstTypeT first_value, RestT... rest)
{
    return first_value + add<RestT...>(rest...);
}

int main(void)
{
    return add(12, 12, 12);
}

However, here is a slight modification. It uses a dependent type in the template definition instead of the template parameter directly:

struct Foo
{
    using SomeType = int;
};

template <typename T>
int add(typename T::SomeType val)
{
    return val;
}

template <typename FirstT, typename... RestT>
int add(typename FirstT::SomeType first_value, typename RestT::SomeType... rest)
{
    return first_value + add<RestT...>(rest...);
}

int main(void)
{
    return add<Foo, Foo, Foo>(12, 12, 12);
}

It compiles and runs as intended using GCC 5.2, but fails using clang 3.8:

clang++ variadic.cpp -o var -std=c++11 -Wall
variadic.cpp:15:26: error: call to 'add' is ambiguous
    return first_value + add<RestT...>(rest...);
                         ^~~~~~~~~~~~~
variadic.cpp:15:26: note: in instantiation of function template specialization 'add<Foo, Foo>' requested here
    return first_value + add<RestT...>(rest...);
                         ^
variadic.cpp:20:12: note: in instantiation of function template specialization 'add<Foo, Foo, Foo>' requested here
    return add<Foo, Foo, Foo>(12, 12, 12);
           ^
variadic.cpp:7:5: note: candidate function [with T = Foo]
int add(typename T::SomeType val)
    ^
variadic.cpp:13:5: note: candidate function [with FirstT = Foo, RestT = <>]
int add(typename FirstT::SomeType first_value, typename RestT::SomeType... rest)
    ^
1 error generated.

My question is twofold.

  1. Is it really a valid use of a parameter pack typename pattern to apply the scope resolution operator to each member of the pack as in typename RestT::SomeType... ?
  2. Is clang correct vis-à-vis the standard, or is this a bug? Is the second example really any more ambiguous than the first? (For the first example, it seems like you could say that the single argument overload is ambiguous with the the second instantiated with RestT = <>)
like image 861
Bobby Moretti Avatar asked Mar 17 '16 04:03

Bobby Moretti


1 Answers

  1. Yes, that's fine.
  2. Current wording is quite clear on this: The parameter pack is completely ignored during partial ordering, because there are no arguments for it ([temp.deduct.partial]/(3.1)). [temp.func.order]/5 also gives a very on point example, even with deducible template arguments - indicating that your first example is also ambiguous:

    [ Note: Since partial ordering in a call context considers only parameters for which there are explicit call arguments, some parameters are ignored (namely, function parameter packs, parameters with default arguments, and ellipsis parameters). [...] [ Example:

    template<class T, class... U> void f(T, U ...);  // #1
    template<class T            > void f(T       );  // #2
    
    void h(int i) {
      f(&i); // error: ambiguous
      // [...]
    }
    

    However, this is not optimal. There is core issue 1395 on variadic template partial ordering:

    CWG agreed that the example should be accepted, handling this case as a late tiebreaker, preferring an omitted parameter over a parameter pack.

    (Issue 1825 gives a more refined strategy.) Both compilers implement this rule for the first case; Only GCC does for the second one (i.e. can be considered half a step ahead).

like image 76
Columbo Avatar answered Nov 15 '22 13:11

Columbo