Gcc Can Compile a Variadic Template While Clang Cannot

gcc can compile a variadic template while clang cannot

Clang's behavior is correct. This is a GCC bug (and the claim in the presentation is also incorrect). §3.3.2 [basic.scope.pdecl]/p1,6:

1 The point of declaration for a name is immediately after its
complete declarator (Clause 8) and before its initializer (if any),
except as noted below.

6 After the point of declaration of a class member, the member name can
be looked up in the scope of its class.

And §3.3.7 [basic.scope.class]/p1 says

The following rules describe the scope of names declared in classes.

1) The potential scope of a name declared in a class consists not only
of the declarative region following the name’s point of declaration,
but also of all function bodies, default arguments,
exception-specifications, and brace-or-equal-initializers of
non-static data members in that class (including such things in nested
classes).

trailing-return-types are not in that list.

The trailing return type is part of the declarator (§8 [dcl.decl]/p4):

declarator:
ptr-declarator
noptr-declarator parameters-and-qualifiers trailing-return-type

and so the variadic version of sum isn't in scope within its own trailing-return-type and cannot be found by name lookup.

In C++14, simply use actual return type deduction (and omit the trailing return type). In C++11, you may use a class template instead and a function template that simply forwards:

template<class T, class... Args>
struct Sum {
static auto sum(T n, Args... rest) -> decltype(n + Sum<Args...>::sum(rest...)) {
return n + Sum<Args...>::sum(rest...);
}
};

template<class T>
struct Sum<T>{
static T sum(T n) { return n; }
};

template<class T, class... Args>
auto sum(T n, Args... rest) -> decltype(Sum<T, Args...>::sum(n, rest...)){
return Sum<T, Args...>::sum(n, rest...);
}

Different results on a variadic template example among gcc, clang and msvc - can anyone explain?

The problem in each case is that the compiler is trying to infer argument_types from the method argument, which is illegal as variadic template parameters can only be inferred when they are at the end of an argument list.

void (foo::*method)(argument_types ... arguments, int, int, int)
^^^^^^^^^^^^^^^^^^ can't infer here
^^^^^^^^^^^^^^^ because of these

The workaround is to protect argument_types from being deduced in this context, using a helper like identity:

template<class T> struct identity { using type = T; };
template<class T> using identity_t = typename identity<T>::type;

// ...

template<typename ... argument_types>
void invoke_foo(foo* instance, int first, int second, int third,
void (foo::*method)(identity_t<argument_types> ... arguments, int, int, int), argument_types ... arguments)
// ^^^^^^^^^^^ fix here

Is this a bug in your code, or in the compilers? Actually, it's a bug in the compilers (yes, all of them); the question is whether a parameter pack appearing within a function type and not at the end of an argument list is a non-deduced context. The relevant part of the standard is [temp.deduct.type], which states:

5 - The non-deduced contexts are: [...]

  • A function parameter pack that does not occur at the end of the parameter-declaration-list.

6 - When a type name is specified in a way that includes a non-deduced context, all of the types that comprise that type name are also non-deduced. However, a compound type can include both deduced and non-deduced types.

Here, argument_types is in a non-deduced context when deducing the type of method, but a deduced context when deducing the types for the trailing arguments of invoke_foo.

Another compiler you could test is ICC (Intel C++ Compiler); ICC rejects the first two forms and accepts the last two, the exact opposite to gcc. The reason that compilers can be so different in their behavior is that dealing with this kind of code is essentially a matter of error handling, specifically recognising when template parameters appear in non-deduced contexts and using the types deduced elsewhere instead. The compilers are (each in their own way) recognising that argument_types cannot be deduced within method, but failing to realise or accept that it can be deduced elsewhere.

Specifically, it appears that:

  • gcc assumes that if it cannot deduce argument_types from method, it must be empty;
  • clang assumes that if argument_types is deduced to be empty or explicitly specified, this must be an error;
  • MSVC is unable to let the deduction of argument_types override the failure to deduce it, but is OK if it is explicitly specified;
  • ICC assumes that if argument_types is deduced to be empty, this must be an error.

Clang vs GCC - Variadic template parameter pack followed by parameter with default value works in GCC 4.8 but not Clang 3.5

This is actually covered by the slightly misplaced [temp.arg.explicit]/3:

A trailing template parameter pack (14.5.3) not
otherwise deduced will be deduced to an empty sequence of template arguments.

The template parameter pack is trailing and thus deduced to the empty pack in all calls except for func<float>(3.3f), that is, they are all valid (and Clang compiles them fine as of 3.5).


However, the compilers are not conforming anymore once we adjust the template's declaration to

template <typename... Args, typename=void>
void func(Args... args, int optional = 0)

Now, the aforementioned quote is not applicable (as Args is not trailing) and [temp.deduct.call]/1 applies instead:

When a function parameter pack appears in a non-deduced context
(14.8.2.5), the type of that parameter pack is never deduced.

(I.e. this should yield a deduction failure.)

Variadic template code compiles on GCC 4.6 but not on clang or GCC 4.7

As part of resolving http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1399, it was decided that a parameter pack that is non-deducible will not participate in deduction, hence keeping the explicitly specified arguments within the parameter list, but not disturbing the matching of subsequent arguments to subsequent function parameters. However those added rules do not necessarily apply for deduction when comparing types (which is a sub-algorithm of the deduction of the set of parameters against the arguments for a function call).

I would argue that the resolution should apply to your situation aswell and regard it as simply an area where the specification is not entirely clear. You may want to send a bug report to the respective compilers and they may want to send an issue report to WG21 for asking to clarify this (although I am not sure I would go as far as saying that this is a normative spec issue - the Standard says elsewhere that non-deducible contexts do not participate in deductions).

Note as A1 is a parameter of the enclosing class template (and hence become normal parameters when the class is instantiated), you can omit them and keep the same meaning as far as template argment deduction is concerned

class Test {
public:
template <class... A2> void print (A2..., float, double) {

}
};

int main () {
Test test;
test.print<int> (1, 1.0f, 1.0); // works
}

Variadic function wont compile with clang

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))

Variadic template as template parameter: deduction works with GCC but not with Clang

Clang is trying to deduce the arguments for this call:

processVariadic(SecondContainer<Element>{});

Since SecondContainer has a default template argument, this is equivalent to:

processVariadic(SecondContainer<Element, Element>{});

Thus, it performs template argument deduction with P = Container<Element> and A = SecondContainer<Element, Element>. It can immediately deduce that the Container template parameter is SecondContainer.

Next, it considers the template arguments. Since the argument type is fully resolved, Clang believes that the parameter must have as many types, or deduction cannot possibly succeed (it doesn't take default arguments into account). So it flags a deduction failure.


So, what's supposed to happen? In the words of [temp.deduct.type]p8,

A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:

[...]
TT<T>
TT<i>
TT<>
where [...] <T> represents template argument lists where at least one argument contains a T, <i> represents template argument lists where at least one argument contains an i and <> represents template argument lists where no argument contains a T or an i.

In order to match the template arguments, we turn to [temp.deduct.type]p9:

If P has a form that contains <T> or <i>, then each argument Pi of the respective template argument list P is compared with the corresponding argument Ai of the corresponding template argument list of A.

There are two things to observe here. One is that this rule does not say what happens if the list Pi and Ai are different lengths (as they are in this case), and the common interpretation seems to be that the mismatched items are not examined. The other is that this rule should not be followed anyway, since the form of P does not contain <T> or <i> (it just contains <>, because there are no template parameters in it).


So, Clang was wrong to reject this code. I've fixed it in r169475.

Nested variadic templates: bug in gcc or clang?

Not sure if it's a bug of gcc or not, but the standard solution to make it compile on gcc is to declare the empty variadic version and then specialize it:

template <typename... T> 
struct Inner;
template <typename Head, typename... Rest>
struct Inner<Head, Rest...> : public Inner<Rest...> { ... };

Demo: http://ideone.com/MFKVY

g++ and clang++ different behaviour deducing variadic template `auto` values

None of the three compilers is correct.

From [temp.param]/17:

If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list. [ Example:

...

template <class... T, T... Values>              // error: Values expands template type parameter
struct static_array; // pack T within the same template parameter list

— end example ]

So the code is ill-formed, even without the line foo(bar<0, 1L>{});.

There is already a Clang bug report and a GCC bug report.



Related Topics



Leave a reply



Submit