Another Bug in G++/Clang? [C++ Templates Are Fun]

Another bug in g++/Clang? [C++ Templates are fun]

Why GCC and Clang think they are right

K, which is the injected class name, has a dual nature in the scope of K<int>. You can use it without template arguments. Then it refers to K<int> (to its own type).

It can be followed by a template argument list too. IMO it's reasonable to say that you need to prefix it with template because of the parser ambiguity with the < that follows. It then refers to the specified type that's determined by the template arguments.

So it can be treated as a member template and as a nested type, depending on whether it's followed by a template argument list. Of course, K is not really a member template. The dual nature of the injected class name seems to me more of a hack anyway, though.

The Standard has an example that reads like this:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
typename Derived::Base b; // error: ambiguous
typename Derived::Base<double> d; // OK
};

One might be inclined to conclude from this that the intent is that you could leave off the template. The Standard says

For a template-name to be explicitly qualified by the template arguments, the name must be known to refer to a template.

I can't see how this wouldn't apply to T::K<T>. If T is a dependent type then you can just lean back because you can't know what K refers to when parsing it, so to make any sense of the code, you just have to be able to prefix it with template. Notice that n3225 has that example too, but it's not a defect there: You can officially leave off template if you lookup into the template's own scope in C++0x (it's called the "current instantiation").

So until now, Clang and GCC are fine.


Why Comeau is right

Just to make it even more complicated, we will have to consider the constructors of K<int>. There is a default constructor and a copy constructor implicitly declared. A name K<int>::K will refer to the constructor(s) of K<int> unless the name lookup used will ignore function (constructor) names. Will typename T::K ignore function names? 3.4.4/3 says about elaborated type specifiers, which typename ... is one of:

If the name is a qualified-id, the name is looked up according its qualifications, as described in 3.4.3, but ignoring any non-type names that have been declared.

However, a typename ... uses different lookup. 14.6/4 says

The usual qualified name lookup (3.4.3) is used to find the qualified-id even in the presence of typename.

The usual qualified lookup of 3.4.3 won't ignore non-type names, as illustrated by the example attached to 14.6/4. So, we will find the constructor(s) as specified by 3.4.3.1/1a (the additional twist that this only happens when non-types are not ignored was added by a later defect report, which all popular C++03 compilers implement though):

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9), the name is instead considered to name the constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition that appears outside of the class definition.

So in the end, I think comeau is correct to diagnose this, because you try to put a template argument list onto a non-template and also violate the requirement quoted in the last part (you use the name elsewhere).

Let's change it by accessing the injected name by a derived class, so no constructor name translation occurs, and you really access the type so that you really can append the template arguments:

// just replace struct X with this:
template<typename T>
struct X
{
struct Derived : T { };
typename Derived::template K<T> *p;
};

Everything compiles now with comeau too! Notice I already did problem report to clang about this exact thing. See Incorrect constructor name resolution. BTW, if you declare a default constructor in K, you can see comeau give a better error message if you use T::K<int>

"ComeauTest.c", line 13: error: overloaded function "N::K<T>::K [with T=int]" is
not a template
typename T::template K<T> *p;

Is invocable and ambiguous call: bug in either g++ or clang

Your reasoning is correct. Note that gcc correctly disallows the call itself:

functor<int, float, char>()(42); // error: base<int> is an ambiguous base

It just incorrectly detects that this invocation is ill-formed. Reported this as gcc bug 84869. T.C. added a further reduced reproduction in the bug report that does not have library dependencies:

struct base {
void operator()(int ) { }
};

struct a : base { };
struct b : base { };

struct f: a, b {
using a::operator();
using b::operator();
};

template<class T> auto g(int) -> decltype(T()(0), 0);
template<class T> auto g(...) -> long;

template<class, class> struct Same;
template<class T> struct Same<T, T> {};

Same<decltype(g<f>(0)), long> s; // should be okay, but gcc errors because it
// thinks decltype(g<f>(0)) is int

g++ and clang++ different behaviour with friend template function defined inside a template class

GCC is right in this case.

The relevant standard wording is in [temp.inst]/2:

The implicit instantiation of a class template specialization causes

— the implicit instantiation of the declarations, but not of the
definitions, of the non-deleted class member functions, member
classes, scoped member enumerations, static data members, member
templates, and friends; and

[...]

However, for the purpose of
determining whether an instantiated redeclaration is valid according
to 6.2 and 12.2, a declaration that corresponds to a definition in the
template is considered to be a definition. [ Example: [...]

template<typename T> struct Friendly {
template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff; // ill-formed: produces second definition of f(U)

— end example ]

The parts related to friends were added to this paragraph by DR2174 and published in C++17 (it's a defect report, so compilers should apply it to previous standard versions as well).


Recent versions of MSVC and EDG in strict mode also reject the code, complaining about a redefinition.

[temp.inject]/1 is somewhat related, but it only talks about friend functions, not friend function templates:

Friend classes or functions can be declared within a class template.
When a template is instantiated, the names of its friends are treated
as if the specialization had been explicitly declared at its point of
instantiation.

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.

Clang couldn't infer template argument whereas gcc / g++ can. Which is right?

My previous answer (now deleted) was wrong. Clang is wrong.

The compiler should be able to deduce the type M because the function argument is M(fun)(T). Note that there is no M in the function pointer argument list, so this corresponds to the T() (C++11) / T(*)() (C++93) in 14.8.2.5:

where (T) represents a parameter-type-list where at least one parameter type contains a T, and () represents a parameter-type-list where no parameter type contains a T.

SFINAE expression fails to compile with clang

I think this is a gcc bug actually, as a result of [temp.class.spec]:

The type of a template parameter corresponding to a specialized
non-type argument shall not be dependent on a parameter of the
specialization. [ Example:

template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error

template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error

—end example ]

In your example, the type of the 3rd template parameter is dependent on a parameter. When you swap it to typename = std::enable_if_t<...>, then this rule no longer applies.


Note: is there any reason to use SFINAE here anyway, as opposed to static_assert-ing?

g++ variadic template issue

Your code is incorrect. The important part of the standard is 6.6.3/1 [basic.start.dynamic]
in N4659:

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an
implicitly or explicitly instantiated specialization [...]

Because the initialization is not ordered, you cannot rely on the order of destruction. Any order is legal, regardless of order of construction. See 6.6.4/3 [basic.start.term]

gcc is thus allowed to destroy s before it destroys a,
which is what happens and causes the weird output. Live.

Pack expansion failure with g++ 4.9.0, but works with clang++ 3.4

Newer versions of clang now reject your code as well, due to the fix for the following bug:

http://llvm.org/bugs/show_bug.cgi?id=18401

Richard Smith's comment on the fix is:

Fix assert by implementing the current proposed direction of core
issue 1430. Don't allow a pack expansion to be used as an argument to an alias
template unless the corresponding parameter is a parameter pack.

The referenced core defect includes, as an example, code which is essentially identical to yours:

template<class... x> class list{};
template<class a, class... b> using tail=list<b...>;
template <class...T> void f(tail<T...>);

int main() {
f<int,int>({});
}

And the defect notes:

There is implementation variance in the handling of this example.

The earlier example given by the defect is a more difficult case that seems to require templates aliases to not behave as transparent aliases for templates. Because of this implementation issue the committee is apparently leaning toward prohibiting using template aliases in certain ways, and compilers implementers seem to be going with that solution.

So my understanding is that this code is valid under the current spec as written, but there are implementation problems and the committee may eventually prohibit it.

GCC and Clang template call resolution differences

According to overload resolution, there are two viable functions: The specialization of the global operator|-template with deduced arguments and the specialization of the member operator function template. Both have the same signature - the member function template has an implicit object parameter of type Alice const& (see §13.3.1/4).

So both viable functions (after template argument deduction) have the same signature. And neither of the templates from which they were instantiated is more specialized than the other. So this is indeed an ambiguity and therefore ill-formed. Surprisingly, VC++ is correct.

How can I force the same behaviour?

Perhaps you should just remove the ambiguity, Clang, VC++ and GCC should have the same behavior then.



Related Topics



Leave a reply



Submit