Deleted template function works on gcc, but not on clang [duplicate] - c++

This question already has an answer here:
Specialized template function with deleted "general" case fails to compile with g++ <=4.8.0 and clang++
(1 answer)
Closed 5 years ago.
I am having trouble with a template specialisation of a deleted template function. The code below shows the problem boiled down to a MWE:
#include <iostream>
#include <string>
template<typename T>
inline std::string typeToString() = delete;
template<>
inline std::string typeToString<float>()
{
return "float";
}
int main()
{
std::cout << typeToString<float>() << std::endl;
}
With gcc 7 this compiles fine. However, with Apple LLVM 8.0.0 I get the following error messages:
clang test.cpp -std=c++1z
test.cpp:8:28: error: inline declaration of 'typeToString<float>' follows non-inline definition
inline std::string typeToString<float>()
^
test.cpp:8:28: note: previous definition is here
test.cpp:15:18: error: call to deleted function 'typeToString'
std::cout << typeToString<float>() << std::endl;
^~~~~~~~~~~~~~~~~~~
test.cpp:8:28: note: candidate function [with T = float] has been explicitly deleted
inline std::string typeToString<float>()

This looks to be a bug. If you compile with clang 3.9.1 or above it will compile. The following examples on Golbolt and Wandbox with clang 3.8.1 fail but when we change to 3.9.1 they both compile.

Related

Resolving compilation errors for c++20 code on Mac

I am trying to setup my machine to be able to compile c++20 code. I have a simple example code
#include <concepts>
#include <iostream>
template<typename T>
requires std::integral<T>
auto gcd(T a, T b) {
if(b == 0) return a;
else return gcd(b, a%b);
}
int main() {
std::cout << "\n";
std::cout << gcd(100,10) << "\n";
}
which I am trying to compile on a MacOS (11.5.1/BigSur).
xcode version - Xcode 12.5.1
Build version 12E507
clang version - Apple clang version 12.0.5 (clang-1205.0.22.11)
compilation command
g++ -std=c++20 myfile.cpp
I get the following error:
myfile.cpp:25:24: error: 'T' does not refer to a value
requires std::integral<T>
^
myfile.cpp:24:19: note: declared here
template<typename T>
^
myfile.cpp:25:15: error: no member named 'integral' in namespace 'std'; did you mean 'internal'?
requires std::integral<T>
~~~~~^~~~~~~~
internal
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ios:960:1: note: 'internal' declared here
internal(ios_base& __str)
^
myfile.cpp:26:1: error: expected expression
auto gcd(T a, T b) {
^
3 errors generated
Need help with this.

C++20 concepts support in CLang

I'm trying to compile an example of some concept from cppreference with Clang on Mac and it fails. How can I fix the issue and compile the example?
Code:
#include <concepts>
#include <iostream>
template<std::regular T>
struct Single {
T value;
friend bool operator==(const Single&, const Single&) = default;
};
int main()
{
Single<int> myInt1{4};
Single<int> myInt2;
myInt2 = myInt1;
if (myInt1 == myInt2)
std::cout << "Equal\n";
std::cout << myInt1.value << ' ' << myInt2.value << '\n';
}
Terminal output:
% g++ -std=c++20 -o a a.cpp
a.cpp:4:15: error: no type named 'regular' in namespace 'std'
template<std::regular T>
~~~~~^
a.cpp:6:5: error: unknown type name 'T'
T value;
^
a.cpp:12:12: error: template argument for non-type template parameter must be an expression
Single<int> myInt1{4};
^~~
a.cpp:4:23: note: template parameter is declared here
template<std::regular T>
^
a.cpp:13:12: error: template argument for non-type template parameter must be an expression
Single<int> myInt2;
^~~
a.cpp:4:23: note: template parameter is declared here
template<std::regular T>
^
4 errors generated.
Compiler version:
% g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.5 (clang-1205.0.22.11)
Target: x86_64-apple-darwin20.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
What's interesting is that the example successfully compiles on GodBolt with lower CLang version (12.0.1)

Member template specialization does not compile with clang

Consider the following program:
struct S {
enum E {
e
};
template<E> void f() = delete;
};
template<> void S::f<S::E::e>() {}
int main() {
S s;
s.f<S::E::e>();
}
GCC 5.4.0 compiles the code, while clang 3.8.0 fails:
$ clang++ -std=c++14 main.cpp
main.cpp:10:20: error: redefinition of 'f'
template<> void S::f<S::E::e>() {
^
main.cpp:8:20: note: previous definition is here
template<> void S::f<S::E::e>();
^
main.cpp:14:11: error: no matching member function for call to 'f'
s.f<S::E::e>();
~~^~~~~~~~~~
main.cpp:5:22: note: candidate template ignored: substitution failure [with $0 = S::E::e]
template<E> void f() = delete;
^
2 errors generated.
Is clang correct and GCC wrong or is it the opposite? Note that if the delete specifier is removed, then clang compiles the code.
this seems defect report Explicit specialization of deleted function template , as you can see from here the issue seems fixed in clang since 3.9.0.

Possible gcc bug with C++14 polymorphic lambdas?

I've found a strange behavior using polymorphic C++14 lambdas (lambdas with auto in their parameters):
Snippet 0:
#include <iostream>
template<typename T> void doLambda(T&& mFn)
{
std::forward<T>(mFn)(int{0});
}
template<typename T> void test(T&& mV)
{
doLambda([&mV](auto mE)
{
std::forward<decltype(mV)>(mV);
});
}
int main() { test(int{0}); return 0; }
clang++ 3.5.1: the snippet compiles and runs successfully.
g++ 4.9.2: the snippet fails to compile:
example.cpp: In instantiation of 'test(T&&)::<lambda(auto:1)> [with auto:1 = int; T = int]':
5 : required from 'void doLambda(T&&) [with T = test(T&&) [with T = int]::]'
13 : required from 'void test(T&&) [with T = int]'
18 : required from here
12 : error: 'mV' was not declared in this scope
std::forward<decltype(mV)>(mV);
^
Compilation failed
Snippet 1:
The only difference from snippet 0 is that the auto inside the lambda was replaced to int.
#include <iostream>
template<typename T> void doLambda(T&& mFn)
{
std::forward<T>(mFn)(int{0});
}
template<typename T> void test(T&& mV)
{
doLambda([&mV](int mE)
{
std::forward<decltype(mV)>(mV);
});
}
int main() { test(int{0}); return 0; }
clang++ 3.5.1: the snippet compiles and runs successfully.
g++ 4.9.2: the snippet compiles and runs successfully.
Snippet 3:
The lambda is now called in-place. auto is still used.
#include <iostream>
template<typename T> void test(T&& mV)
{
[&mV](auto mE)
{
std::forward<decltype(mV)>(mV);
}(int{0});
}
int main() { test(int{0}); return 0; }
clang++ 3.5.1: the snippet compiles and runs successfully.
g++ 4.9.2: the snippet compiles and runs successfully.
Why is g++ complaining about snippet 0? Is there anything wrong in my code? Is this a known bug or should I submit this?
As stated in the comments, this behavior is indeed a gcc bug.

C++ GCC Why this sfinae code can be compiled with GCC 4.7, but not with 4.8?

I like to use local classes in template classes to perform constructions like "static if". But I've faced with the problem that gcc 4.8 does not want to compile my code. However 4.7 does.
This sample:
#include <type_traits>
#include <iostream>
#include <string>
using namespace std;
struct A {
void printA() {
cout << "I am A" << endl;
}
};
struct B {
void printB() {
cout << "I am B" << endl;
}
};
template <typename T>
struct Test {
void print() {
struct IfA {
constexpr IfA(T &value) : value(value) {
}
T &value;
void print() {
value.printA();
}
};
struct IfB {
constexpr IfB(T &value) : value(value) {
}
T &value;
void print() {
value.printB();
}
};
struct Else {
constexpr Else(...) {}
void print() {
}
};
typename conditional<is_same<T, A>::value, IfA, Else>::type(value).print();
typename conditional<is_same<T, B>::value, IfB, Else>::type(value).print();
}
T value;
};
int main() {
Test<A>().print();
Test<B>().print();
}
Options:
g++ --std=c++11 main.cc -o local-sfinae
Task:
Given classes A and B with different interfaces for printing.
Write a generic class Test that can print both A and B.
Do not pollute either any namespace or class scope.
Description of the code:
This is only a clean example.
I use an approach like this, because I want to generalize the construction "static if". See, that I pass the arguments to IfA and IfB classes via their fields, not directly to the print() function.
I use such constructions a lot.
I've found that these constructions should not be in (pollute) class scope. I mean they should be placed in a method scope.
So the question.
This code can not be compiled with GCC 4.8. Because it checks ALL classes, even if they are never used. But it has not instantiate them in binary (I've commented the lines that cause errors and compiled it with gcc 4.8). Proof:
$ nm local-sfinae |c++filt |grep "::If.*print"
0000000000400724 W Test<A>::print()::IfA::print()
00000000004007fe W Test<B>::print()::IfB::print()
See, there is no Test::print()::IfB::print(). (See later: 'void Test::print()::IfB::print() [with T = A]')
The errors if I compile aforementioned code with gcc 4.8:
g++ --std=c++11 main.cc -o local-sfinae
main.cc: In instantiation of 'void Test<T>::print()::IfB::print() [with T = A]':
main.cc:36:9: required from 'void Test<T>::print() [with T = A]'
main.cc:49:21: required from here
main.cc:34:17: error: 'struct A' has no member named 'printB'
value.printB();
^
main.cc: In instantiation of 'void Test<T>::print()::IfA::print() [with T = B]':
main.cc:28:9: required from 'void Test<T>::print() [with T = B]'
main.cc:50:21: required from here
main.cc:26:17: error: 'struct B' has no member named 'printA'
value.printA();
^
Is it a GCC 4.8 bug?
Or is it GCC 4.7 bug? Maybe the code should not be compiled.
Or it is a my bug, and I should not rely on the compiler behavior/should not use such approach to implement "static if".
Additional info:
This simple code compiles on 4.7, but not on 4.8. I shortened it.
struct A {
void exist() {
}
};
template <typename T>
struct Test {
void print() {
struct LocalClass {
constexpr LocalClass(T &value) : value(value) {
}
T &value;
void print() {
value.notExist();
}
};
}
T value;
};
int main() {
Test<A>().print();
}
Errors:
main.cc: In instantiation of 'void Test<T>::print()::LocalClass::print() [with T = A]':
main.cc:16:9: required from 'void Test<T>::print() [with T = A]'
main.cc:22:21: required from here
main.cc:14:17: error: 'struct A' has no member named 'notExist'
value.notExist();
^
Have tested two GCC 4.8 versions: 2012.10 and 2013.02. Hope it is GCC 4.8 bug and it can be fixed.
LocalClass is not a template. The "not instantiated if not used" rule is only applicable to member functions of class templates.
That is, when Test::print() is instantiated, everything that is inside is brought to life, including the unused member of its local class.
There is no SFINAE in your code.
SFINAE applies during template argument deduction and argument substitution (the 'S' in SFINAE stands for substitution) but the only substitution in your program happens when substituting A for T in the template parameter list of Test, which doesn't fail.
You then call print() which instantiates Test<A>::print(), which doesn't involve any substitution, and you get an error because value.notExist(); is not valid.
SFINAE has to be used in substitution contexts, such as template argument deduction caused by a function call or when deducing template parameters with default arguments.