PYA
PYA

Reputation: 8636

Variadic function wont compile with clang

First off, I apologize if this is a duplicate, I will be happy to take it down but I am not even sure what the issue/diagnosis is here.

Anyway, my code here works with gcc and not clang - why is this the case? I am clearly not able to understand why clang cant compile this.

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;


template <typename T, typename... Args, typename std::enable_if<!sizeof...(Args)>::type* = nullptr>
void func(int start_size, int idx)
{
    cout << start_size << " " << idx << endl;
    return;
}

template <typename T, typename... Args, typename std::enable_if<sizeof...(Args)>::type* = nullptr>
void func(int start_size, int idx)
{

    if((idx + 1) == int(start_size - int(sizeof...(Args))))
       {
           cout << start_size << " " << idx << endl;
           return;
       }
       func<Args...>(start_size, idx);
}

template <typename... Args>
void func_wrapper(int idx)
{
    func<Args...>(sizeof...(Args),idx);
}


int main()
{
    func_wrapper<int,double,char>(1);

}

Error:

prog.cc:37:5: error: no matching function for call to 'func'
    func<Args...>(sizeof...(Args),idx);
    ^~~~~~~~~~~~~
prog.cc:44:5: note: in instantiation of function template specialization 'func_wrapper<int, double, char>' requested here
    func_wrapper<int,double,char>(1);
    ^
prog.cc:16:6: note: candidate template ignored: requirement '!sizeof...(Args)' was not satisfied [with T = int, Args = <double, char>]
void func(int start_size, int idx)
     ^
prog.cc:23:6: note: candidate template ignored: substitution failure [with T = int, Args = <double, char>]: non-type template argument evaluates to 2, which cannot be narrowed to type 'bool'
void func(int start_size, int idx)
     ^
1 error generated.

Wandbox: https://wandbox.org/permlink/yqki47uYcwUlE013

Upvotes: 3

Views: 356

Answers (2)

Jarod42
Jarod42

Reputation: 218323

In addition of the missing explicit conversion from std::size_t to bool which would require code as

std::enable_if<sizeof...(Args) != 0>

There is an other error:

temp#res-8.3

The program is ill-formed, no diagnostic required, if:

[..]

every valid specialization of a variadic template requires an empty template parameter pack,

So

template <typename T,
          typename... Args,
          typename std::enable_if<sizeof...(Args) == 0>::type* = nullptr>

is invalid.
(There is also void* = nullptr which might be problematic).

You may write it with 2 overloads instead:

template <typename T>
void func(int start_size, int idx)
{
    cout << start_size << " " << idx << endl;
}

template <typename T, typename T2, typename... Args> // Addition of T2, and change sizeof...
void func(int start_size, int idx)
{
    if ((idx + 1) == int(start_size - int(1 + sizeof...(Args)))) {
        cout << start_size << " " << idx << endl;
        return;
    }
    func<T2, Args...>(start_size, idx);
}

Upvotes: 1

llllllllll
llllllllll

Reputation: 16434

clang is correct in rejecting this code. To eliminate the error, you should explicitly apply a conversion:

std::enable_if<bool(sizeof...(Args))>

The reasoning is:

[temp.arg.nontype]/5

The following conversions are performed on each expression used as a non-type template-argument. If a non-type template-argument cannot be converted to the type of the corresponding template-parameter then the program is ill-formed.

(5.1) For a non-type template-parameter of integral or enumeration type, conversions permitted in a converted constant expression ([expr.const]) are applied.

Then [expr.const]/3 says that a narrowing conversion is not considered automatically:

A converted constant expression of type T is a literal constant expression, implicitly converted to type T, where the implicit conversion (if any) is permitted in a literal constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions ([conv.lval]), integral promotions ([conv.prom]), and integral conversions ([conv.integral]) other than narrowing conversions ([dcl.init.list])

Thus for this case, a narrowing conversion from std::size_t to bool, it should be explicit: bool(sizeof...(Args))

Upvotes: 7

Related Questions