Compiler error when using integer as template parameter - c++

What is wrong with the following piece of code?
template<typename X>
struct A {
template<int N>
int foo() const {
return N;
}
};
template<typename X>
struct B {
int bar(const A<X>& v) {
return v.foo<13>();
}
};
#include <iostream>
using std::cout;
using std::endl;
int main() {
A<double> a;
B<double> b;
cout << b.bar(a) << endl;
return 0;
}
Inside the function B::bar the compiler complains:
error: invalid operands of types
‘’ and ‘int’ to binary ‘operator<’
If A is not a template, everything compiles fine.

Change return v.foo<13>(); to return v.template foo<13>(); because foo is a dependent name and you need to mention that explicitly using .template construct.

Related

Why can't the compiler deduce the return type if argument type depends on a template parameter

I am getting a template deduction error when trying to choose a function of an overload set (foo) within a template based on another template parameter:
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x, foo);
}
I get the following error:
In function 'int main()':
27:15: error: no matching function for call to 'print(IntData&, <unresolved overloaded function type>)'
27:15: note: candidate is:
15:6: note: template<class T, class R> void print(const T&, R (*)(typename T::type))
15:6: note: template argument deduction/substitution failed:
27:15: note: couldn't deduce template parameter 'R'
As template deduction should proceed from left to right, my assumption was that once the type of T is deduced, the type of R should also be deducable. Actually, I can get rid of the error when calling print like
print<IntData>(x, foo);
which seems to show that R can actually be deduced once T is known. So why doesn't it work if both parameter should be deduced?
Thank you!
I believe it is because you have R as the return type for your function pointer argument.
Note this quote from a previous question
So, when we ask about the signature of a function, we have to give two answers:
For functions that are specializations of function templates, the
signature includes the return type.
For functions that are not specializations, the return type is not part of the signature.
Since foo is simply an overloaded function and void is not part of the foo function signature, R will not assist the compiler in deducing correct function overload. Therefore, the use of foo as a function pointer is ambiguous within the scope of main. The compiler usually resolves the overload by matching the types of the provided arguments, for which there are none when the function pointer is by itself.
I believe this is the most robust solution, to include an intermediary function to resolve the previously ambiguous function pointer. I included some other types in addition to int to demonstrate the flexibility of using auto with the strategies mentioned below.
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int" << std::endl;
}
void foo(double a) {
std::cout << "double" << std::endl;
}
bool foo(char a) {
std::cout << "char" << std::endl;
return true;
}
template <typename T, typename R>
R print(const T& data, R(*fun)(typename T::type) ) {
return fun(data.value);
}
struct IntData{
using type = int;
type value;
};
struct DoubleData{
using type = double;
type value;
};
struct CharData{
using type = char;
type value;
};
template <typename T>
auto print2(const T& data)
{
auto(*fooResolved)(typename T::type) = foo;
return print(data,fooResolved);
}
int main()
{
IntData x{1};
print2(x);
DoubleData y{1.0};
print2(y);
CharData z{'a'};
bool result = false;
std::cout << "bool before: " << result << std::endl;
result = print2(z);
std::cout << "bool after : " << result << std::endl;
}
Here are a few more potential solutions to help illustrate the problem:
(note the change is removing R as the second template argument)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T>
void print(const T& data, void(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x, foo);
}
As well as this (passing the value directly, which allows for multiple return types)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R (*fun)(T) ) {
fun(data);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x.value, foo);
}
And to further illustrate the original issue (see the return type is now deduced)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int" << std::endl;
}
bool foo(double a) {
std::cout << "double" << std::endl;
return true;
}
template <typename T, typename R>
R print(const T& data, R (*fun)(T) ) {
return fun(data);
}
struct IntData{
using type = int;
type value;
};
struct DoubleData{
using type = double;
type value;
};
int main()
{
IntData x{1};
print(x.value, foo);
//foo(int) does not return a value
//bool test = print(x.value, foo); // Does not compile
DoubleData y{1.0};
bool result = false;
result = print(y.value, foo);
std::cout << result << std::endl;
}
And while we're at it, you could also resolve them given the original code by explicitly specifying which foo you want
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
void(*fooResolved)(int) = foo;
print(x, fooResolved);
}

type trait for function pointer?

I need to conditionally use either std::abs or std::fabs inside template class, here is relevant code in simplified version:
template <typename T>
class C
{
public:
using type = std::conditional_t<std::is_integral_v<T>, std::uint64_t, long double>;
using check = std::is_integral<type>;
// ERROR: mismatch in format parameter list
constexpr auto ptr_abs = check::value ? &std::abs<check::value_type> : &std::fabs;
// use pointer
void use_ptr()
{
auto x = (*ptr_abs)(-3);
}
};
None of the attempts worked for me, I'm clueless.
int main()
{
C<int> a;
a.f();
C<float> b;
b.f();
}
Do you really need to work with function pointers? Wouldn't be better to exploit C++ type-safe mechanisms? Such as follows:
template <typename T>
class C
{
public:
using type = std::conditional_t<std::is_integral_v<T>, std::uint64_t, long double>;
static const bool check = std::is_integral_v<type>;
std::function<type(type)> abs = [](auto arg)
{
if constexpr (check) return std::abs(static_cast<long long>(arg));
else return std::fabs(arg);
};
void use()
{
auto x = abs(-3);
}
};
This works for me well. Just note that there is no std::abs for unsigned integers, therefore, to avoid ambiguity, I had to choose a particular overload by casting (to long long in this example; I don't know what is Result).
Before C++17, where there is no if constexpr, you can achieve the same just with some more typing by using template specializations.
Resolve the function overload with the type of the pointer:
#include <cmath>
#include <type_traits>
#include <cstdlib>
#include <iostream>
template <typename T>
class C {
public:
static constexpr T (*ptr_abs)(T) = &std::abs;
void f() {
std::cout << typeid(ptr_abs).name() << "\n";
auto x = (*ptr_abs)(-3);
}
};
int main()
{
C<int> a;
a.f(); // PFiiE
C<float> b;
b.f(); // PFffE
C<double> c;
c.f(); // PFddE
}
Maybe I've misunderstood your problem, but it seems to me that you could separately define your version of abs that behaves as you want and then use it inside other classes
#include <cmath>
#include <cstdint>
#include <complex>
#include <iostream>
#include <limits>
#include <type_traits>
#include <typeinfo>
namespace my {
template <class T>
auto abs_(T x)
{
if constexpr ( std::is_unsigned_v<T> ) {
return static_cast<uintmax_t>(x);
}
else if constexpr ( std::is_integral_v<T> ) {
return static_cast<uintmax_t>(std::abs(static_cast<intmax_t>(x)));
}
else {
return std::fabs(static_cast<long double>(x));
}
}
template <class T>
auto abs_(std::complex<T> const& x)
{
return std::abs(static_cast<std::complex<long double>>(x));
}
}
template <typename T>
class C
{
public:
void use(T x)
{
std::cout << typeid(T).name() << ' ' << x;
auto a = my::abs_(x);
std::cout << ' ' << typeid(a).name() << ' ' << a << '\n';
}
};
int main()
{
C<int> a;
a.use(-42);
C<float> b;
b.use(-0.1);
C<long long> c;
c.use(std::numeric_limits<long long>::min());
C<size_t> d;
d.use(-1);
C<std::complex<double>> e;
e.use({-1, 1});
}
Testable here.

Failed Implicit Static Member Initialization by Visual Studio

Take the following snippet
#include <iostream>
using namespace std;
template<int i, typename T = int> struct A
{
T num = i;
A<i, T>()
{
cout << "Instantiated a A<" << i << ">" << endl;
}
};
template<int i, int i2> struct B
{
static A<i> a;
static A<i * i2> a2;
};
template<int i, int i2> A<i> B<i, i2>::a{};
template<int i, int i2> A<i * i2> B<i, i2>::a2{};
template<typename T> struct C
{
static void doSomething()
{
cout << "Have a A<" << T::a.num << "> and a A<" << T::a2.num << "> in C" << endl;
}
};
int main() {
typedef C<B<2, 2>> c;
cout << "Typedefined a C\nCalling static member function to initialize C<B<2, 2>>'s B<2, 2>'s A<>s" << endl;
c::doSomething();
return 0;
}
Now with gcc, then this compiles (both C++11 and C++14) and instantiates a and a2 as expected.
Thanks to WhozCraig, this also compiles with clang.
However with Visual C++ (2015 edition), I get a parse error.
main.cpp(37) error C2143: syntax error: missing ';' before '<end Parse>'
Followed by some notes
main.cpp(19): note: while compiling class template static data member 'A<2,int> B<2,2>::a'
main.cpp(26): note: see reference to class template instantiation 'B<2,2>' being compiled
main.cpp(25): note: while compiling class template member function 'void C<B<2,2>>::doSomething(void)'
main.cpp(33): note: see reference to function template instantiation 'void C<B<2,2>>::doSomething(void)' being compiled
main.cpp(33): note: see reference to class template instantiation 'C<B<2,2>>' being compiled
What is going on here?
I was able to reduce the test case, identify the problem (which appears to be a bug in the Visual C++ compiler) and find a workaround:
#include <iostream>
using namespace std;
template<int i> struct A
{
int num = i;
A() {}
};
template<int i> struct B
{
static A<i> a;
};
// MSVC doesn't like this syntax
// |||
// vvv
template<int i> A<i> B<i>::a{};
// To fix the error, rewrite the above line in one of the below ways:
//
// template<int i> A<i> B<i>::a;
// template<int i> A<i> B<i>::a = {};
int main() {
typedef B<2> B2;
cout << B2::a.num << endl;
return 0;
}

Type trait for extracting template parameter from nested type

I need to get the template parameter from a nested type. Here is a simple example to show the type I need to extract.
#include <iostream>
#include <typeinfo>
template<typename T>
void function(T) {
// T = 'struct A<int>::B'
//
// Here I want to get the template value type e.g. 'int' from T
// so that this would print 'int'. How can this be done?
std::cout << typeid(T).name() << std::endl;
}
template<typename T>
struct A {
using B = struct { int f; };
};
int main() {
function(A<int>::B{});
return 0;
}
You can't extract this through simple deduction. Although B is a nested class of A, the types themselves are unrelated.
One option would be to "save" the type inside B and extract it later:
template<typename T>
struct A {
struct B {
using outer = T;
int f;
};
};
Then you just use typename T::outer to get the type:
template<typename T>
void function(T) {
std::cout << typeid(typename T::outer).name() << std::endl;
}

SFINAE not working although template methods are used

Consider the following code that tries to use SFINAE for providing different method implementations depending on a template parameter.
#include <type_traits>
#include <iostream>
template<bool S>
struct C{
template<typename std::enable_if<!S>::type* = nullptr>
int foo(int i){
return i + 1;
}
template<typename std::enable_if<S>::type* = nullptr>
int foo(int i){
return i;
}
};
int main(){
C<true> c1;
C<false> c2;
std::cout << c1.foo(0) << c2.foo(0) << std::endl;
}
This example is inspired by the reference page of std::enable_if. As you see, struct C<S> has two foo methods. One should be enabled if S is true, and the other if S is false. However, the code does not compile but raises the following errors:
src/test.cpp: In instantiation of ‘struct C<true>’:
src/test.cpp:19:12: required from here
src/test.cpp:7:8: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
int foo(int i){
^
src/test.cpp: In instantiation of ‘struct C<false>’:
src/test.cpp:20:13: required from here
src/test.cpp:12:8: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
int foo(int i){
So it seems that the compiler completelty ignores SFINAE and raises an error once it finds that a type is not enabled. What am I doing wrong here?
S is not a template parameter of the method, it is a template parameter of the class. During instantiation of the class, S has been determined, and as a result, std::enable_if<!S_>::type is no longer type-dependent so cannot be used the way you're using it. You can, as Nawaz answered, solve this by using overloading, but you can also make S a template parameter of the method -- sort of:
#include <type_traits>
#include <iostream>
template<bool S>
struct C {
template<bool S_ = S, typename std::enable_if<!S_>::type* = nullptr>
int foo(int i){
return i + 1;
}
template<bool S_ = S, typename std::enable_if<S_>::type* = nullptr>
int foo(int i) {
return i;
}
};
int main(){
C<true> c1;
C<false> c2;
std::cout << c1.foo(0) << c2.foo(0) << std::endl;
}
template<typename std::enable_if<!S>::type* = nullptr>
int foo(int i)
{
return i + 1;
}
This is NOT function template, because there is NO template parameter for this (supposedly) function template. It is not even function. The code is simply ill-formed.
Note that S is a template parameter of the enclosing class template, not the function (template).
The following code would be correct (but wouldn't solve your problem):
template<typename SS, typename std::enable_if<!SS>::type* = nullptr>
int foo(int i)
{
//etc
}
Here typename SS defines template parameter for the function template. Your function doesn't do so. The template parameter used in std::enable_if must be a template parameter of the same function template.
Use function overloading to solve your problem:
template<bool S>
struct C
{
int foo(int i)
{
return foo_impl(std::integral_constant<bool, S>(), i);
}
private:
int foo_impl(std::true_type, int i)
{
return i + 1;
}
int foo_impl(std::false_type, int i)
{
return i;
}
};
That is the general implementation. But in this specific case, when you're using bool as template parameter, then another solution could be this:
template<bool S>
struct C
{
int foo(int i)
{
return S ? foo_a(i) : foo_b(i);
}
private:
int foo_a(int i)
{
return i + 1;
}
int foo_b(int i)
{
return i;
}
};
Since S is known to the compiler, so I believe the compiler would eliminate the branching in return S ? foo_a(i) : foo_b(i); and would instead write either return foo_a(i); or return foo_b(i); based on the value of S, effectively producing faster code.