A couple of questions for C++11 experts.
I'm fighting with SFINAE and I came across a strange case in which g++ (4.9.2), and clang++ (3.5.0) behave differently.
I have prepared the following sample code. I'm sorry but I'm unable to do it significantly more concise.
#include <string>
#include <iostream>
#include <typeinfo>
#include <type_traits>
template <typename X>
class foo
{
private:
template <typename R>
using enableIfIsInt
= typename std::enable_if<std::is_same<X, int>::value, R>::type;
public:
foo ()
{ }
template <typename R = void>
enableIfIsInt<R> bar ()
{ std::cout << "bar: is int\n"; }
void bar ()
{
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
<< typeid(enableIfIsInt<void>).name() << "}\n";
}
};
int main ()
{
foo<long> fl;
foo<int> fi;
fl.bar();
fi.bar();
return 0;
}
My idea was to create a template foo<X> class that (via SFINAE) can define a method in one or in another way depending on the X template argument.
The program compile well with g++ 4.9.2 but clang++ 3.5.0 give the following error
test.cpp:13:36: error: no type named 'type' in
'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable
this declaration
= typename std::enable_if<std::is_same<X, int>::value, R>::type;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:26:23: note: in instantiation of template type
alias 'enableIfIsInt' requested here
<< typeid(enableIfIsInt<void>).name() << "}\n";
^
test.cpp:36:7: note: in instantiation of member function
'foo<long>::bar' requested here
fl.bar();
^
1 error generated.
I suppose that is right clang++ but my first question to C++11 experts is: who right? g++ or clang++?
About the g++ produced program output, it's the following
bar: isn't int; is [i]{v}
so g++ seems to ignore the fl.bar(); instruction.
Now a little change: i modify the second version of foo<X>::bar() in this way
void bar ()
{ std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; }
deleting the std::enable_if inside the function abomination. Now both g++ and clang++ are compiling without problems and the output, for both compiled versions of the program, is
bar: isn't int; is [l]
bar: isn't int; is [i]
So, my second question is: what I'm doing wrong? Why, in the int case, I don't obtain the "is int" version of foo<X>::bar()?
Be patient with me if I'm doing some foolish: I'm trying to learn C++11.
And sorry for my bad English.
clang's error isn't coming from the substitution failure. It's coming from here:
void bar ()
{
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
<< typeid(enableIfIsInt<void>).name() << "}\n"; // <==
}
enableIfIsInt<void> isn't in the immediate context, that's a hard failure for X is not int. You simply can't use that expression in that context.
Once you remove that - the non-template bar() is always called. That's because both functions are equivalent matches and non-templates are preferred to templates in overload resolution.
So the real solution is to use tag-dispatching:
void bar() { bar(std::is_same<X, int>{}); }
void bar(std::true_type ) {
std::cout << "bar: is int\n";
}
void bar(std::false_type ) {
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n";
}
with which both compilers happily yield:
bar: isn't int; is [l]
bar: is int
Related
In C++20, I defined a concept AllIntegral with a non-type template parameter pack auto... T_values.
Whilst, GCC 10.1.0 accepts its usage in some contexts, it refuses to compile its usage in others, particularly in an
if statement. The associated error message says 'AllIntegral' does not constrain a type.
My code looks like this:
#include <concepts>
#include <ios>
#include <iostream>
template<auto... T_values>
concept AllIntegral = (std::integral<decltype(T_values)> && ...);
int main()
{
std::cout << std::boolalpha << AllIntegral<1, 2> << '\n'; // compiles and prints "true"
if (AllIntegral<1, 2>) std::cout << "true" << '\n'; // does not compile
std::cout.flush();
}
This is the compiler output:
main.cpp: In function ‘int main()’:
main.cpp:11:9: error: ‘AllIntegral’ does not constrain a type
11 | if (AllIntegral<1, 2>) std::cout << "true" << '\n';
| ^~~~~~~~~~~~~~~~~
main.cpp:6:9: note: concept defined here
6 | concept AllIntegral = (std::integral<decltype(T_values)> && ...);
What's the reason for this error? Why is my concept not usable in a boolean context?
I've been told that logic AND (&&) in template doesn't work, so I want to use template specialization to achive it.
My test code like follows:
#include <iostream>
template <bool b1, bool b2>
constexpr static bool andVal = false;
// template specialization
template <bool b2>
constexpr static bool andVal<true, b2> = b2;
int main(int argc, char *argv[]) {
std::cout << andVal<1, 1> << std::endl;
std::cout << andVal<0, 1> << std::endl;
std::cout << andVal<0, 0> << std::endl;
std::cout << andVal<1, 0> << std::endl; // this line will cause compilation error
return 0;
}
But when I compile the code, error occured like this:
/tmp/ccaqDdfO.s: Assembler messages:
/tmp/ccaqDdfO.s:369: Error: symbol `_ZL6andVal' is already defined
If I comment the last line test code std::cout << andVal<1, 1> << std::endl;, the compilation will success and the test result is correct.
What's wrong with the template function? and why it is already defined?
Any reply will be appreciated!
The template is fine. This is a bug in gcc versions 5.4 through 6.1: template specialization compile error. You happen to be on the bottom edge of the bugged version range.
Why do gcc HEAD 10.0.0 20190 and Clang HEAD 9.0.0 both reject this program?
#include <iostream>
void g( int x )
{
std::cout << "Hello g( " << x << " )\n";
}
template <int N>
void g()
{
std::cout << "Hello g<N>( " << N << " )\n";
}
namespace N
{
using ::g;
}
void g( int x = 20 );
template <int N = 10>
void g();
int main()
{
N::g();
N::g<>();
}
For example gcc issues the error
prog.cc: In function 'int main()':
prog.cc:27:11: error: no matching function for call to 'g()'
27 | N::g<>();
| ^
prog.cc:9:6: note: candidate: 'template<int N> void g()'
9 | void g()
| ^
prog.cc:9:6: note: template argument deduction/substitution failed:
prog.cc:27:11: note: couldn't deduce template parameter 'N'
27 | N::g<>();
| ^
though according to the C++ 20 (and 17) Standard (9.8 The using declaration)
11 [Note: For a using-declaration whose nested-name-specifier names a
namespace, members added to the namespace after the using-declaration
are not in the set of introduced declarations, so they are not
considered when a use of the name is made. Thus, additional overloads
added after the using-declaration are ignored, but default function
arguments (9.2.3.6), default template arguments (13.1), and template
specializations (13.6.5, 13.8.3) are considered. — end note]
Based on my reading of the standard, I believe this is a bug.
This issue is the subject of Core issue 1907. The current direction is to treat such cases as ill-formed, no diagnostic required.
As it turns out, some implementations track default arguments on a "per-entity" basis (so it would be difficult to not consider default arguments added in a later redeclaration), while others track them on a "per-declaration" basis (so it would difficult to make them consider such default arguments). CWG decided to accommodate both implementation strategies by classifying code that depends on such things IFNDR.
I think the problem is that you're re-declaring the g<int> template function after implicitly declaring it during its definition.
This minimised example also fails to compile. Note no namespaces involved:
#include <iostream>
template <int N>
void g()
{
std::cout << "Hello g<N>( " << N << " )\n";
}
// redeclaration
template <int N = 10>
void g();
int main()
{
g<>();
}
whereas this compiles:
#include <iostream>
// declaration
template <int N = 10>
void g();
// definition
template <int N>
void g()
{
std::cout << "Hello g<N>( " << N << " )\n";
}
int main()
{
g<>();
}
I read book < C++ Templates - the Complete Guide > and learned template specialization for pointer. (maybe I misunderstand this part of the book)
(1) Here's my simple template:
#include <iostream>
template<typename T>
void Function(const T& a)
{
std::cout << "Function<T>: " << a << std::endl;
}
template<typename T>
void Function<T*>(const T* a)
{
std::cout << "Function<T*>: " << a << std::endl;
}
int main(void)
{
Function(1);
Function(1.2);
Function("hello");
Function((void*)0x25);
return 0;
}
I use ubuntu16.04 x64, g++ 5.3, the compiler report:
$ g++ main.cpp -o main.exe
main.cpp:10:29: error: non-type partial specialization ‘Function<T*>’ is not allowed
void Function<T*>(const T* a)
(2) but this code is correct:
#include <iostream>
template<typename T>
void Function(const T& a)
{
std::cout << "Function<T>: " << a << std::endl;
}
int main(void)
{
Function(1);
Function(1.2);
Function("hello");
Function((void*)0x25);
return 0;
}
result shows:
$ g++ main.cpp -o main.exe
$ ./main.exe
Function<T>: 1
Function<T>: 1.2
Function<T>: hello
Function<T>: 0x25
My question is: Is the book about pointer specialization is wrong ? Or I mis understand the meaning of this part in the book ? Or something else ?
Update about pointer specialization in class.
(3) template class with pointer specialization:
#include <iostream>
template<typename T>
struct Base {
T member;
Base(const T& a)
: member(a)
{
}
void hello()
{
std::cout << member << std::endl;
}
};
template<typename T>
struct Base<T*> {
T* member;
Base(T* a)
: member(a)
{
}
void hello()
{
std::cout << member << std::endl;
}
};
int main(void)
{
Base<int> b1(12);
Base<double> b2(2.4);
Base<char*> b3("hello");
Base<void*> b4((void*)0x25);
b1.hello();
b2.hello();
b3.hello();
b4.hello();
return 0;
}
this code is correct with one warning:
$ g++ main.cpp -o main.exe
main.cpp: In function ‘int main()’:
main.cpp:37:27: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
Base<char*> b3("hello");
^
$ ./main.exe
12
2.4
hello
0x25
(4) template class without pointer specialization:
#include <iostream>
template<typename T>
struct Base {
T member;
Base(const T& a)
: member(a)
{
}
void hello()
{
std::cout << member << std::endl;
}
};
int main(void)
{
Base<int> b1(12);
Base<double> b2(2.4);
Base<char*> b3("hello");
Base<void*> b4((void*)0x25);
b1.hello();
b2.hello();
b3.hello();
b4.hello();
return 0;
}
result is the same:
$ g++ main.cpp -o main.exe
main.cpp: In function ‘int main()’:
main.cpp:39:27: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
Base<char*> b3("hello");
^
$ ./main.exe
12
2.4
hello
0x25
Does this means pointer specialization is needless ?
Or maybe this feature behave differently on different compiler ?
as you've been already told, partial specialization of function templates are not allowed. You can use std::enable_if for this:
template <typename T, typename std::enable_if_t<!std::is_pointer<T>::value>* = 0>
void func(T val) { std::cout << val << std::endl; }
template <typename T, typename std::enable_if_t<std::is_pointer<T>::value>* = 0>
void func(T val) { func(*val); }
If you are looking for simpler syntax, wait for concepts
The error message told you what is wrong:
non-type partial specialization ‘Function<T*>’ is not allowed
You can only partially specialize types (classes). You've tried to partially specialize a function. Functions are not types; you can only fully specialize them.
Two problems:
You are not allowed to partially specialise a function.
The behaviour of (void*)0x25 is undefined. With the exception of nullptr, you are not allowed to set a pointer to memory you don't own, with the exception of one past the final element of an array and one past the address of a scalar.
I had some code that was failing to compile, which amounts to something
like what's shown below. After some digging around, I came across
paragraph 14.1 note 5, which states:
The top-level cv-qualifiers on the template-parameter are ignored
when determining its type.
My code looks like this:
#include <iostream>
#include <typeinfo>
class Bar {};
template<class T>
void Func(T t)
{
std::cout << typeid(T).name() << "\n";
}
template<class T>
void Func(const T& t)
{
std::cout << "const ref : " << typeid(T).name() << "\n";
}
int main()
{
Bar bar;
const Bar& constBar = bar;
Func(constBar);
return 0;
}
It gives this compilation error:
In function 'int main()'
error: call of overloaded 'Func(const Bar&)' is ambiguous
Can someone comment on the reasoning behind the this rule in the standard?
The problem with your code is that the function call is ambiguous. The const Bar & can match either the value or the const reference. G++ says:
xx.cpp:24: error: call of overloaded 'Func(const Bar&)' is ambiguous
This has nothing specifically to do with templates - you would get the same error if you overloaded a non-template function.
And as people have told you here time after time, you will not learn C++ by reading the Standard.
As you could have found easily yourself, this has nothing to do with templates. This
class Bar {};
void Func(Bar) {}
void Func(const Bar&) {}
int main()
{
Bar bar;
const Bar& constBar = bar;
Func(bar);
Func(constBar);
return 0;
}
gives the same errors.
The call is ambiguous because anything can match T or const T &.
Just try Func(0);: it will give the same error message.