class Base {
int i;
};
template <auto V>
struct Foo {
int a;
};
int main()
{
struct Foo<&Base::i> struct_foo;
Foo<&Base::i> foo;
}
The fist line in main() compiles in gcc 12.2, but the second line doesn't compile. What is the reason?
https://godbolt.org/z/eTG7cW35q
This seems to be a gcc bug as both statements should fail compilation(which they do with clang and msvc).
Demo
Related
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.
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.
I'm having troubles with template expression parameters. My code is running well in gcc and clang but it's failing to compile in Visual Studio with the error: "unable to match function definition to an existing declaration."
I narrowed problematic code to something very small but I can't understand the reason of the error. It seems that using Dim in expression parameter of the return type is causing the problems. I fixed it by adding typedef Foo<Dim> Foo inside Bar but I want to know the reason for the error. Should that code compile? Is there any reason the typedef makes a difference?
#include <iostream>
template <int _Dim>
struct Foo{
enum {Dim = _Dim};
int i = Dim;
};
template <typename T>
struct Bar{
enum {Dim = T::Dim};
Foo<Dim> fun();
};
template<typename T>
Foo<Bar<T>::Dim> Bar<T>::fun()
{
return Foo<Bar<T>::Dim>();
}
int main() {
Bar<Foo<3>> myBar;
std::cout <<myBar.fun().i <<std::endl;
return 0;
}
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"; }
};
I have some trouble compiling a piece of C++ code using Clang 3.3 on linux. However the same piece of code compiles with gcc 4.8.2 as well as Intel Compiler. So I wanted if my code is actually legal. Usually I trust clang more with such questions ;)
Anyway, so here is the code fragment:
namespace test {
template<class SCALAR=double>
struct Foo {
public:
template<class SCALAR_ARG>
friend Foo<SCALAR_ARG> create_Foo( );
typedef SCALAR scalar_t;
};
template<class SCALAR_ARG=double>
Foo<SCALAR_ARG> create_Foo( )
{
typedef Foo<SCALAR_ARG> impl_t;
return impl_t();
}
}
struct Dummy {
typedef Dummy impl_t;
};
int main() {
typedef test::Foo<Dummy> foo_t;
typedef typename foo_t::scalar_t scalar_t;
Dummy egv_;
test::create_Foo();
return 0;
}
What do you think? Should I post it as bug in Clang or is it actually ill-formed?
Thanks in Advance,
Raffael
It would be good to see what error you actually get with clang.
You have a lot of noise in the code you posted, parts that
are unrelated to the problem.
Here is how you should deal with template friends.
Following the advice given there, here is how I fixed your code:
namespace test {
template <class > struct Foo;
template <class T=double> Foo<T> create_Foo();
template<class SCALAR=double>
struct Foo {
public:
friend Foo create_Foo<>( );
typedef SCALAR scalar_t;
};
template<class SCALAR_ARG>
Foo<SCALAR_ARG> create_Foo( )
{
typedef Foo<SCALAR_ARG> impl_t;
return impl_t();
}
}
struct Dummy {
typedef Dummy impl_t;
};
int main() {
typedef test::Foo<Dummy> foo_t;
typedef typename foo_t::scalar_t scalar_t;
Dummy egv_;
test::create_Foo();
return 0;
}
It compiles both with gcc 4.7.2 and clang++ 3.4 (trunk).