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.
Related
#include <array>
#include <iostream>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace a::b::c
{
inline constexpr std::string_view str{ "hello" };
}
template <class... T>
std::tuple<std::size_t, std::common_type_t<T...>> sum(T... args)
{
return { sizeof...(T), (args + ...) };
}
int main()
{
auto [iNumbers, iSum]{ sum(1, 2, 3) };
std::cout << a::b::c::str << ' ' << iNumbers << ' ' << iSum << '\n';
std::array arr{ 1, 2, 3 };
std::cout << std::size(arr) << '\n';
return 0;
}
When I run this program I'm getting this error.
1>D:\Programming\CPP\LearningCPP\LearningCPP.cpp(28,9): error C2440:
'return': cannot convert from 'initializer list' to
'std::tuple<size_t,std::common_type<int,int,int>>'
1>D:\Programming\CPP\LearningCPP\LearningCPP.cpp(27,1): message : No
constructor could take the source type, or constructor overload
resolution was ambiguous
1>D:\Programming\CPP\LearningCPP\LearningCPP.cpp(33): message : see
reference to function template instantiation
'std::tuple<size_t,std::common_type<int,int,int>>
sum<int,int,int>(int,int,int)' being compiled
This is source code which is used in Learncpp.com . Actually I was having difficult understanding and I'm getting these errors, I have changed the compiler version to c++17 still getting these errors and the code can someone help me by explain the code thanks
This example code:
#include <string>
#include <iostream>
template <int i>
struct Wrapper
{
static const std::string _str;
typedef const Wrapper<i+1> NextType_t; // template recursion
static NextType_t _nxt;
typedef const Wrapper<i-1> PrevType_t; // template recursion
static PrevType_t _prev;
};
template<int i>
const std::string Wrapper<i>::_str = std::to_string(i);
template<int i>
typename Wrapper<i>::NextType_t Wrapper<i>::_nxt;
template<int i>
typename Wrapper<i>::PrevType_t Wrapper<i>::_prev;
// recursion termination - lower bound
template <>
struct Wrapper<-1>
{
static const std::string _str;
typedef const Wrapper<0> NextType_t;
static NextType_t _nxt;
typedef const Wrapper<-1> PrevType_t;
static PrevType_t _prev;
};
const std::string Wrapper<-1>::_str = std::to_string(-1);
typename Wrapper<-1>::NextType_t Wrapper<-1>::_nxt;
typename Wrapper<-1>::PrevType_t Wrapper<-1>::_prev;
// recursion termination - upper bound
template <>
struct Wrapper<UPPER_LIMIT>
{
static const std::string _str;
typedef const Wrapper<-1> NextType_t;
static NextType_t _nxt;
typedef const Wrapper<UPPER_LIMIT-1> PrevType_t;
static PrevType_t _prev;
};
const std::string Wrapper<UPPER_LIMIT>::_str = std::to_string(UPPER_LIMIT);
typename Wrapper<UPPER_LIMIT>::NextType_t Wrapper<UPPER_LIMIT>::_nxt;
typename Wrapper<UPPER_LIMIT>::PrevType_t Wrapper<UPPER_LIMIT>::_prev;
int
main(
int argc,
char **)
{
Wrapper<0> wrapperFirst;
Wrapper<UPPER_LIMIT> wrapperLast;
// here's the list
std::cout << wrapperFirst._str << std::endl;
std::cout << wrapperFirst._nxt._str << std::endl;
std::cout << wrapperFirst._nxt._nxt._str << std::endl;
// [...]
// and the final element
std::cout << wrapperLast._str << std::endl;
std::cout << wrapperLast._prev._str << std::endl;
std::cout << wrapperLast._prev._prev._str << std::endl;
// [...]
// and the tailing NIL
std::cout << Wrapper<UPPER_LIMIT>::NextType_t::_str << std::endl;
return 0;
}
fails for gcc:
> g++ -std=c++11 -DUPPER_LIMIT=100 -ftemplate-depth=500 -o test main.cpp
main.cpp: In instantiation of ‘struct Wrapper<499>’:
main.cpp:24:33: recursively required from ‘struct Wrapper<1>’
main.cpp:24:33: required from ‘struct Wrapper<0>’
main.cpp:43:47: required from here
main.cpp:24:33: fatal error: template instantiation depth exceeds maximum of 500 (use -ftemplate-depth= to increase the maximum)
typename Wrapper<i>::NextType_t Wrapper<i>::_nxt;
^~~~~~~~~~
compilation terminated.
but succeeds for clang:
> clang++ -std=c++11 -DUPPER_LIMIT=100 -ftemplate-depth=500 -o test main.cpp
> ./test
0
1
2
100
99
98
-1
Is there something wrong in the code? I wonder that gcc wants to go beyond the UPPER_LIMIT, because there is a terminating specialization for that.
Wrapper<-1> is instantiated on this line
typename Wrapper<-1>::NextType_t Wrapper<-1>::_nxt;
This causes Wrapper<0> to be instantiated, which causes Wrapper<1> to be instantiated, etc. At that point in the code, the specialization for Wrapper<UPPER_LIMIT> has not been defined, so this causes an infinite recursion.
Moving the definition of the Wrapper<UPPER_LIMIT> specialization above the Wrapper<-1>::_nxt definition solves the problem.
Apparently Clang defers instantiation, so that this problem doesn't arise.
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.
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
The following code compiles fine in GCC but in Visual Studio it results in
error C2782: 'bool contains(const T &,const std::initializer_list<T2>
&)' : template parameter 'T' is ambiguous see declaration of
'contains' could be 'const wchar_t *' or 'std::wstring'
It does however compile and work if the order of the template parameters is given as
template<typename T2, typename T>
Is this a compiler bug?
#include <string>
#include <iostream>
#include <set>
#include <initializer_list>
#include <algorithm>
template<typename T, typename T2>
bool contains(T const& value, std::initializer_list<T2> const& set)
{
return std::find(std::begin(set), std::end(set), value) != std::end(set);
}
int main(void)
{
std::set<std::wstring> values = { L"bar", L"not" };
for (std::wstring val : values) {
std::wcout << "\"" << val << "\" ";
if (contains(val, { L"foo", L"bar", L"baz", L"doom" })) {
std::wcout << "found" << std::endl;
}
else {
std::wcout << "not found" << std::endl;
}
}
}
Edit: I have created a bugreport: https://connect.microsoft.com/VisualStudio/feedbackdetail/view/982338/template-parameter-order-matters
I remember that VS has a bug where they would do double-deduction in certain scenarios, and I think that's what's happening here. Clang also compiles it both ways, so since clang + gcc agree, it's likely a VS bug.
I had a similar problem which was resolved by switching to the latest VS Pro version. I think this bug was addressed in the latest VS pro version as I remember seeing it in a change-log at some point.