C++ 17: Using alias template bug in gcc? - c++

Snippet:
#include <functional>
template <typename T>
struct CallableTrait;
template <typename R, typename... Args>
struct CallableTrait<std::function<R(Args...)>>
{
using ReturnType = R;
};
template <typename Callable>
using CallableTraitT = CallableTrait<decltype(std::function{std::declval<Callable>()})>;
template <typename Callable>
auto test(Callable&&)
{
using CallableInfo = CallableTraitT<Callable>;
static_assert(!std::is_void_v<typename CallableInfo::ReturnType>);
}
int main()
{
test([]() { return 42; });
return 0;
}
Demo
This compiles fine with clang-12.0.0 and MSVC-19.16.27034, but gcc-11.0.0 throws an error:
prog.cc: In instantiation of 'auto test(Callable&&) [with Callable = main()::<lambda()>]':
prog.cc:25:29: required from here
prog.cc:20:25: error: invalid use of incomplete type 'struct CallableTrait<main()::<lambda()> >'
20 | static_assert(!std::is_void_v<typename CallableInfo::ReturnType>);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:5:8: note: declaration of 'struct CallableTrait<main()::<lambda()> >'
5 | struct CallableTrait;
|
Who is right and who isn't?
EDIT:
Bug is tracked here gcc-bugzilla

The issue is tracked here gcc-bugzilla

Related

CTAD doesn't work with defaulted template arguments?

Compare the following case when I have a class object that takes a vector. The non-deduced parameter T can be substituted fine with the default template argument:
#include <vector>
template <typename T = int>
struct container
{
container(std::vector<T> vec) {}
};
int main()
{
container C = std::vector{1,2,3,4,5};
}
This is not the case for my class which is a bit more complicated (CompilerExplorer):
#include <cstdio>
#include <initializer_list>
#include <variant>
template <size_t> struct obj;
template<size_t Vs>
using val = std::variant<std::monostate, int, struct obj<Vs>>;
template <size_t Vs = 0>
struct obj
{
obj() = default;
obj(std::initializer_list<val<Vs>> init) {
printf("initializer of object called, Vs = %d\n", Vs);
}
};
template <size_t Vs = 0>
struct container : public obj<Vs>
{
container(obj<0> init) {}
};
int main()
{
container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
}
This fails with the following error:
<source>: In function 'int main()':
<source>:29:57: error: class template argument deduction failed:
29 | container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
| ^
<source>:29:57: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
But it works when I supplement the template specialization obj<0> in the instantiation of the container (in main). Any ideas why this doesn't work for my class and how I can fix it? I don't want to force the user to specify the template each time.
This problem already exists in the simpler case of just
auto o = obj{1,2,33};
which yields this error:
<source>:29:24: error: class template argument deduction failed:
29 | auto o = obj{1,2,33};
| ^
<source>:29:24: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
<source>:14:5: note: template argument deduction/substitution failed:
<source>:29:24: note: mismatched types 'std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >' and 'int'
29 | auto o = obj{1,2,33};
So, the compiler is unable to deduce, that the three ints should be an initializer list. If you add extra braces around them, the compiler recognizes that this should actually be a single list argument instead of three separate ones and it works:
auto o = obj{{1,2,33}};
This also carries over to the more complicated case:
container some_container = obj{{1,2,5,2,obj{{1,2,33}},2,2}};

Template argument deduction fails when passing enum

I'm trying to insert an enum parameter into a constexpr function. I have done this in the past and it always worked... except in this case. This case is only special in that I'm calling a factory function first. But apparently C++ doesn't see through this. What can be done?
Those are my errors:
<source>: In function 'constexpr auto operator+(some_enum)':
<source>:30:28: error: no matching function for call to 'signal<1, state>(std::nullptr_t)'
30 | return signal<1, state>(nullptr);
| ~~~~~~~~~~~~~~~~^~~~~~~~~
<source>:23:37: note: candidate: 'template<bool set, some_enum S, class ... Ts> constexpr signal_str<sizeof... (Ts)> signal(Ts ...)'
23 | constexpr signal_str<sizeof...(Ts)> signal(Ts... Args)
| ^~~~~~
<source>:23:37: note: template argument deduction/substitution failed:
<source>:30:28: error: 'state' is not a constant expression
30 | return signal<1, state>(nullptr);
| ~~~~~~~~~~~~~~~~^~~~~~~~~
<source>:30:28: note: in template argument for type 'some_enum'
<source>:28:16: error: invalid return type 'auto' of 'constexpr' function 'constexpr auto operator+(some_enum)'
28 | constexpr auto operator+(some_enum state)
| ^~~~~~~~
Compiler returned: 1
This is my code:
#include <array>
#include <cstdint>
#include <iostream>
typedef void* TaskType_t;
enum some_enum
{
SOME_STATE = 1,
};
template <size_t N>
struct signal_str
{
uint32_t val_;
std::array<TaskType_t, N> tasks_;
};
template <bool set, some_enum S, typename... Ts>
constexpr signal_str<sizeof...(Ts)> signal(Ts... Args)
{
return signal_str<sizeof...(Ts)>{S, {Args...}}.val_;
}
constexpr auto operator+(some_enum state)
{
return signal<1, state>(nullptr);
}
int main()
{
static_assert(+SOME_STATE);
}
I'm using C++17 on xtensa-gcc 8.2.0 but it's the same with gcc 11 (LIVE DEMO).
EDIT: This problem is different from "Why is const variable necessary for template specialization over constants" because enums are already constants. To showcase this the following DOES actually compile:
#include <array>
#include <cstdint>
#include <iostream>
typedef void* TaskType_t;
enum some_enum
{
SOME_STATE = 1,
};
template <size_t N>
struct signal_str
{
uint32_t val_;
std::array<TaskType_t, N> tasks_;
};
constexpr auto operator+(some_enum state)
{
return signal_str<1>{state, nullptr}.val_;
}
int main()
{
static_assert(+SOME_STATE);
}
DEMO
So the problem IMHO is not the enum..

c++ type traits : ensuring a subclass implements a method

There is a virtual class C.
I would like to ensure that any concrete subclass inheriting from C implements a function "get" (and have a clear compile time error if one does not)
Adding a virtual "get" function to C would not work in this case, as C subclasses could implement get functions of various signatures.
(in the particular case I am working on, pybind11 will be used to creates bindings of the subclasses, and pybind11 is robust of the "get" method of B to have a wide range of signatures)
Checking at compile time if a class has a function can be done with type traits, e.g.
template<class T>
using has_get =
decltype(std::declval<T&>().get(std::declval<int>()));
My question is where in the code should I add a static assert (or smthg else) to check the existence of the "get" function. Ideally, this should be part of C declaration, as things should be easy for new user code inheriting from it. It may also be that a completely different approach would be better, which I'd like to hear.
Not sure what standard you are using but with C++20 you can do something like this using concepts
template<typename T>
concept HasGet = requires (T a)
{
a.get();
};
template<HasGet T>
void foo(T x)
{
x.get();
}
struct Foo
{
int get() {
return 1;
}
};
struct Bar
{
};
int main()
{
foo(Foo{});
foo(Bar{});
}
Error:
<source>: In function 'int main()':
<source>:27:12: error: use of function 'void foo(T) [with T = Bar]' with unsatisfied constraints
27 | foo(Bar{});
| ^
<source>:8:6: note: declared here
8 | void foo(T x)
| ^~~
<source>:8:6: note: constraints not satisfied
<source>: In instantiation of 'void foo(T) [with T = Bar]':
<source>:27:12: required from here
<source>:2:9: required for the satisfaction of 'HasGet<T>' [with T = Bar]
<source>:2:18: in requirements with 'T a' [with T = Bar]
<source>:4:9: note: the required expression 'a.get()' is invalid
4 | a.get();
EDIT:
As C++14 is preferred, if I understand you requirements, this is something you can do in C++14
#include <type_traits>
#include <utility>
using namespace std;
template<typename... Ts>
using void_t = void;
template<typename T, typename = void>
struct has_get
: false_type
{};
template<typename T>
struct has_get<T, void_t<decltype(declval<T>().get())>>
: true_type
{};
template<typename T>
static constexpr auto has_get_v = has_get<T>::value;
struct P
{
};
struct C1 : P
{
int get()
{
return 1;
}
};
struct C2 : P
{
float get()
{
return 1.0F;
}
};
struct C3
{
bool get()
{
return true;
}
};
template<typename T>
enable_if_t<is_base_of<P, decay_t<T>>::value && has_get_v<decay_t<T>>> foo(T x)
{
x.get();
}
int main()
{
foo(C1{});
foo(C2{});
foo(C3{});
}
ERROR:
<source>: In function 'int main()':
<source>:61:11: error: no matching function for call to 'foo(C3)'
61 | foo(C3{});
| ^
<source>:52:77: note: candidate: 'template<class T> std::enable_if_t<(std::is_base_of<P, typename std::decay<_Tp>::type>::value && has_get<typename std::decay<_Tp>::type>::value)> foo(T)'
52 | enable_if_t<is_base_of<P, decay_t<T>>::value && has_get<decay_t<T>>::value> foo(T x)
| ^~~
<source>:52:77: note: template argument deduction/substitution failed:
In file included from <source>:1:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]':
<source>:52:77: required by substitution of 'template<class T> std::enable_if_t<(std::is_base_of<P, typename std::decay<_Tp>::type>::value && has_get<typename std::decay<_Tp>::type>::value)> foo(T) [with T = C3]'
<source>:61:11: required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2554:11: error: no type named 'type' in 'struct std::enable_if<false, void>'
2554 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;

Make Templates for basic data types only

How can we make a template accept only basic data types.
template <typename T>
void GetMaxValue( T& x )
{
//... Finds max Value
}
In the above function GetMaxValue we are able to pass any value without any an error.
But the std Function std::numeric_limits<T>::max() has handled it.
For example:
auto max = std::numeric_limits< std::map<int,int> >::max();
will Give an error error C2440: '<function-style-cast>' : cannot convert from 'int' to 'std::map<_Kty,_Ty>'
With constraints in C++20:
#include <type_traits>
template <class T>
requires std::is_arithmetic_v<T>
void GetMaxValue( T& x )
{
//... Finds max Value
}
Usage:
int a = 0;
GetMaxValue(a); // fine
std::vector<int> b;
GetMaxValue(b); // compiler error
Demo
With std::enable_if otherwise:
template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
void GetMaxValue( T& x )
{
//... Finds max Value
}
Demo 2
The error messages pre-constraints are harder to read:
error: no matching function for call to 'GetMaxValue(std::vector<int>&)'
| GetMaxValue(b); // compiler error
| ^
Note: candidate: 'template<class T, typename std::enable_if<is_arithmetic_v<T>, int>::type <anonymous> > void GetMaxValue(T&)'
| void GetMaxValue( T& x )
| ^~~~~~~~~~~
note: template argument deduction/substitution failed:
error: no type named 'type' in 'struct std::enable_if<false, int>'
| template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
| ^
In instantiation of 'void GetMaxValue(T&) [with T = int; typename std::enable_if<is_arithmetic_v<T>, int>::type <anonymous> = 0]'
vs
error: cannot call function 'void GetMaxValue(T&) [with T = std::vector<int>]'
| GetMaxValue(b); // compiler error
| ^
note: constraints not satisfied
In function 'void GetMaxValue(T&) [with T = std::vector<int>]':
required by the constraints of 'template<class T> requires is_arithmetic_v<T> void GetMaxValue(T&)'
note: the expression 'is_arithmetic_v<T>' evaluated to 'false'
| requires std::is_arithmetic_v<T>
| ~~~~~^~~~~~~~~~~~~~~~~~

using std::result_of to determine the return type of a template argument

I think the snippet of code is self explanatory, but basically the template function ExecFunc should be able to execute another function and return its result. I know I can achieve similar results using decltype instead of result_of, but this question is to understand why what I've written does not work: the snippet does not compile on gcc v4.9.2.
This is what I have:
#include <type_traits>
int f(int i)
{
return i;
}
template<class F, class T>
auto ExecFunc(F f, T arg) -> typename std::result_of<F()>::type
{
return f(arg);
}
int main() {
auto a = ExecFunc(f, 3);
return 0;
}
and this is the compiler output:
prova.cpp: In function ‘int main()’:
prova.cpp:15:26: error: no matching function for call to ‘ExecFunc(int (&)(int), int)’
auto a = ExecFunc(f, 3);
^
prova.cpp:15:26: note: candidate is:
prova.cpp:9:6: note: template<class F, class T> typename std::result_of<F()>::type ExecFunc(F, T)
auto ExecFunc(F f, T arg) -> typename std::result_of<F()>::type
^
prova.cpp:9:6: note: template argument deduction/substitution failed:
prova.cpp: In substitution of ‘template<class F, class T> typename std::result_of<F()>::type ExecFunc(F, T) [with F = int (*)(int); T = int]’:
prova.cpp:15:26: required from here
prova.cpp:9:6: error: no type named ‘type’ in ‘class std::result_of<int (*())(int)>’
N.B.
this question might look like a duplicate of this one but the accepted solution doesn't work for me (at least, as far as I can tell I have incorporated the solution in my code).
The function you have is int f(int i) but you are calling F() which is unknown. std::result_of<F()>::type should be std::result_of<F(T)>::type.
Live Example
The problem is with the parameter of result_of, it should be:
-> typename std::result_of<F(T)>::type
This is the perfect time to use decltype
template<class F, class T>
auto ExecFunc(F f, T arg) -> decltype(f(arg))