"Nested" class-template argument deduction with parentheses: GCC vs. clang - c++

Related, but (IMHO) different: Nested template argument deduction for class templates not working
The following C++17 code is rejected from GCC 8, but clang compiles it without any issues. The GCC's error message is included as a comment just before the problematic line.
Which compiler is correct here?
https://godbolt.org/z/WG6f7G
template<class T>
struct Foo {
Foo(T) {}
};
template<class T>
struct Bar {
Bar(T) {};
};
void works() {
Bar bar{1};// {}
Foo foo(bar);// ()
}
void works_too() {
Foo foo{Bar{1}};// {{}}
}
void error_in_gcc() {
// error: 'auto' parameter not permitted in this context
Foo foo(Bar{1});// ({})
}
void but_this_works() {
Foo(Bar{1});// ({})
}

Comments to this question state that this is a GCC bug. It has been filed as GCC bug report 89062.

Related

Why is GCC unable to deduce class template arguments of an braced init expression passed to a templated constructor? [duplicate]

Related, but (IMHO) different: Nested template argument deduction for class templates not working
The following C++17 code is rejected from GCC 8, but clang compiles it without any issues. The GCC's error message is included as a comment just before the problematic line.
Which compiler is correct here?
https://godbolt.org/z/WG6f7G
template<class T>
struct Foo {
Foo(T) {}
};
template<class T>
struct Bar {
Bar(T) {};
};
void works() {
Bar bar{1};// {}
Foo foo(bar);// ()
}
void works_too() {
Foo foo{Bar{1}};// {{}}
}
void error_in_gcc() {
// error: 'auto' parameter not permitted in this context
Foo foo(Bar{1});// ({})
}
void but_this_works() {
Foo(Bar{1});// ({})
}
Comments to this question state that this is a GCC bug. It has been filed as GCC bug report 89062.

Clang claims that `member reference base type 'X' is not a structure or union`, but X is a structure template with deduced parameters

Consider following code:
template <typename T> struct X
{
X(T) {}
void foo() {}
};
template <typename T> struct Y
{
int object = 0;
void bar()
{
X(object).foo();
}
};
Live on gcc.godbold.org
GCC 8.2 compiles it, while Clang 7 spits out following error:
<source>:13:18: error: member reference base type 'X' is not a structure or union
X(object).foo();
~~~~~~~~~^~~~
This looks like a bug to me.
The conditions are very specific: If either structure is not a template, or if object is not a member variable, or if CTAD (class template argument deduction) is not involved, then Clang compiles the code as well.
What's going on here? Is it indeed a Clang bug?
And what's more important, how can I make the code compile with minimal changes, preferrably without getting rid of CTAD?
The only flag used is -std=c++17.
clang++ --version is
clang version 7.0.0 (tags/RELEASE_700/final 342594)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Yes, this is clang bug see Class template argument deduction with deduced type fails when accessing member which says:
Try to compile the following c++ program:
template <class T>
struct C {
C(T) {}
int a;
};
template <class T>
int foo(T v) {
return C{v}.a; // <----
}
int main() {
foo(4);
}
The line marked above fails with the error:
error: member reference base type 'C' is not a structure or union
return (C{v}).a;
~~~~~~^~
The bug report also specifies cases that do work, which may or may not be alternatives.
Note that the following all work fine:
template <class T>
C<T> foo(T v) {
return C{v};
}
and
int foo(int v) {
return C{v}.a;
}
and
C{4}.a;
I tried this also on a recent trunk build (trunk 346600)

Return type deduction for template in-class friend functions in clang

So up front, what I'm trying to do is not an really intended use case for in-class friend definitions. But it does work under g++ and, best I can tell, is supposed to work according to C++14 specs.
For the purposes of discussion, clang is 5.0.0 and gcc is 7.2.0, although I've tested with other recent and HEAD versions and got identical results. Everything compiled with c++14 flag.
The minimal reproduction of case I'm interested in is bellow.
#include <iostream>
auto foo();
template <typename T>
class Foo
{
friend auto foo()
{
return (T)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo() << std::endl;
return 0;
}
Gcc behavior: Compiles and runs. Outputs: 3.14
Clang behavior: Refuses to compile. Error: conflicting types for 'foo'
That in itself seems completely wrong, but since auto doesn't give clang any problems when I take away templatization of Foo, I decided to try and give the foo function a dummy template.
#include <iostream>
template <typename X = int>
auto foo();
template <typename T>
class Foo
{
template <typename X>
friend auto foo()
{
return (T)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo() << std::endl;
return 0;
}
Gcc behavior: Compiles and runs. Outputs: 3.14
Clang behavior: Compiles and runs. Outputs: 3
And yes, when compiled with clang, type deduction of foo depends entirely on default template parameter in declaration, despite not appearing anywhere in the definition. o_O
Finally, I decided to try and fix this particular behavior by throwing in a using directive.
#include <iostream>
template <typename X = int>
auto foo();
template <typename T>
class Foo
{
using Type = T;
template <typename X>
friend auto foo()
{
return (Type)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo() << std::endl;
return 0;
}
Gcc behavior: Compiles and runs. Outputs: 3.14
Clang behavior: Compiler crashes. Every version I've tried.
The crash is an obvious bug, but I'm more interested in the first two examples. Is this a bug in clang as well? Or is there some subtle reason for why this should be undefined behavior, and I'm just getting lucky with gcc? And more importantly, can anyone think of a workaround that would work on both gcc and clang? The thing I'm trying to do is detect the type with which Foo was instantiated, provided that it is unique, at compile time from outside of Foo.
While I'm still not entirely sure why Clang behaves this way, I have found a workaround that does compile on both Clang and GCC. The later raises a warning, but it can either be suppressed, or slight variation of definitions can be introduced with ifdefs to make the code compile cleanly on both.
#include <iostream>
template <typename T = void>
class A
{
public:
friend auto foo(A<T>);
};
template <typename T>
class Foo
{
friend auto foo(A<void>)
{
return (T)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo(A{}) << std::endl;
return 0;
}
This allows deduction of return type of foo() to be based on template parameter with which Foo was instantiated, even if they happen in different blocks of code, which is precisely what I was looking for.

Template friendship error compilation with GCC but not with clang

This code compiles with clang 3.7.1 (with no diagnostic) but fails with GCC 5.3.0 (live example):
#include <iostream>
template<typename T>
struct A {
void foo()
{
static_cast<T*>(this)->implementation();
}
};
struct Crtp : A<Crtp> {
template<typename T>
friend struct A;
private:
void implementation() { std::cout << "implementation()\n"; }
};
int main()
{
Crtp c;
c.foo();
}
GCC's error message is the following:
main.cpp:13:16: error: specialization of 'A' after instantiation
friend struct A;
Which one is right and why? Is it a bug of GCC / clang?
Seems to be an old g++ bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52625).
Reported and never corrected, if I understand correctly,
I think this is gcc's bug.
A template friend class declaration is only a declaration, not a definition.
Redeclaration of class template is allowed unless it has different class-key (see N4527 14.5.1.4).
Specialization or instantiation can occur twice or more.
Explicit specialization can occur only once(N4527 14.7.3.6).
Then, gcc's diagnostics is odd because there is no explicit specialization.
We do have some template name resolution odds:
struct Crtp : A<Crtp> {
A x; // A refers to A<Crtp>
};
Now things are clear:
template<typename T> friend struct A;
refers to:
template<typename T> friend struct A<Crtp>;
which is... yeah, partial specialization (really tricky one).
So GCC is correct here.
What do you really need is:
struct Crtp : A<Crtp> {
friend struct A;
private:
void implementation() { std::cout << "implementation()\n"; }
};

Does static_assert require use of template parameter?

I'm trying to disallow references with a class I've got and I'm seeing some strange behavior. I've built a toy example that shows what's happening. If I have this:
template <class T>
struct something {
};
template <class T>
struct something<T&> {
static_assert(false, "reference disallowed with something");
};
int main() {
something<int> a; (void)a;
}
Even though I'm not declaring an instance of something with a reference, it still fails:
> g++ -std=c++11 foo.cc -o foo
foo.cc:7:5: error: static assertion failed: reference disallowed with something
static_assert(false, "reference disallowed with something");
^
If I tweak it so that it has to use the template parameter through a proxy class, then it works:
template <class T>
struct something {
};
template <class T>
struct something<T&> {
template <class TT> struct falsity {
static const bool value = false;
};
static_assert(falsity<T>::value, "reference disallowed with something");
};
int main() {
something<int> a; (void)a;
}
Then it works just fine, is this the expected behavior? I would have thought the the static assert would be a member of the class regardless.
Edit: this is gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
Your static_assert does not depend on any template parameters and so the compiler does not have to wait until instantiation of the class template to evaluate the statement. Instead it does so during the first phase of two phase name lookup and your code fails to compile.
In your case you can fix the failure by changing the assertion to
static_assert(!std::is_lvalue_reference<T>::value, "reference disallowed with something");