How do you declare a generic type of an expression in a `requires` constraint? - c++

I may be asking a wrong question here, but what exactly am I doing wrong that it causes the compiler to think that the constraint I'm expecting on pop method of the stack is std::same_as<void, T>?
#include <concepts>
#include <stack>
template <typename S, typename T>
concept generic_stack = requires(S s, T t) {
s.push(t);
{ s.pop() } -> std::same_as<T>; // error seems to stem from here
};
template <typename T, generic_stack<T> S>
void compile_if_stack(S) {}
int main() {
compile_if_stack<int>(std::stack<int>{});
}
I tried std::same_as<decltype(s.pop()), T>; and it appears to work, but what I don't understand is what's wrong with the former approach.
Full error
# clang 12.0.1
$ clang++ -std=c++20 main.cpp
main.cpp:14:5: error: no matching function for call to 'compile_if_stack'
compile_if_stack<int>(std::stack<int>{});
^~~~~~~~~~~~~~~~~~~~~
main.cpp:11:6: note: candidate template ignored: constraints not satisfied [with T = int, S = std::stack<int>]
void compile_if_stack(S) {}
^
main.cpp:10:23: note: because 'generic_stack<std::stack<int>, int>' evaluated to false
template <typename T, generic_stack<T> S>
^
main.cpp:7:25: note: because type constraint 'std::same_as<void, int>' was not satisfied:
{ s.pop() } -> std::same_as<T>; // error seems to stem from here
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/concepts:63:19: note: because '__detail::__same_as<void, int>' evaluated to false
= __detail::__same_as<_Tp, _Up> && __detail::__same_as<_Up, _Tp>;
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/concepts:57:27: note: because 'std::is_same_v<void, int>' evaluated to false
concept __same_as = std::is_same_v<_Tp, _Up>;
^
1 error generated.
C++ compiler from GCC 11.1.0 comes up with semantically identical error message.

This is because stack.pop() returns void, as per documented in std::stack::pop.
The constraint is not right, you should check for top instead:
template <typename S, typename T>
concept generic_stack = requires(S s, T t) {
s.push(t);
{ s.top() } -> std::same_as<T const&>;
};

std::stack does not return a value on pop. It has a separate function for that. Namely, top. So pop() is a void function. Hence the error.

Related

Tagged structures casting rules

I was playing around with toy tuple implementations and eventually stuck with how get function works.
Consider this simple example
#include <iostream>
#include <utility>
template <size_t Tag, typename ValueType>
struct TagedValue { ValueType value; };
struct Test : TagedValue<0, int>, TagedValue<1, std::string>, TagedValue<2, double> {};
template <size_t Idx, typename T>
auto& get(Test& test) {
((TagedValue<Idx, T>&)(test)).value;
}
template <size_t Idx, typename T>
auto& get_impl(TagedValue<Idx, T>& tagged_value) {
return tagged_value.value;
}
template <size_t Idx>
auto& get_2(Test& test) {
return get_impl<Idx>(test);
}
int main()
{
Test test;
get_2<0>(test);
get<0>(test);
}
I get this error:
<source>: In function 'int main()':
<source>:29:16: error: no matching function for call to 'get<0>(Test&)'
29 | get<0>(test);
| ^
<source>:10:7: note: candidate: 'template<long unsigned int Idx, class T> auto& get(Test&)'
10 | auto& get(Test& test) {
| ^~~
<source>:10:7: note: template argument deduction/substitution failed:
<source>:29:16: note: couldn't deduce template parameter 'T'
29 | get<0>(test);
| ^
I do have couple of questions:
Basically why get_2 works and get doesn't compile. To me it looks like get_2 does exactly what I'm trying to do inside get
Does deducing T for get_2 take O(1) time, if yes how is it possible? Does compiler store some kind of map internally?
When you call a function template then all template arguments must either be specified explicitly or be deduced from the function arguments. When you call
get<0>(test);
Then Idx is 0, but there is no way for the compiler to know what T is supposed to be. The parameter is just Test, and T cannot be deduced from that.

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;

g++ fails to resolve template function overload

With the following code g++ fails:
template <typename X = int, typename T, typename ...R>
inline void func(const T&, R...) {}
template <typename T>
struct S {};
template <typename X = int, typename T, typename ...R>
inline void func(const S<T>&, R...) {}
int main() {
func(42);
func(S<int>()); // OK
func(S<int>(), 1); // NOK
func<int>(S<int>(), 1); // NOK
}
with:
<source>: In function 'int main()':
<source>:13:21: error: call of overloaded 'func(S<int>, int)' is ambiguous
func(S<int>(), 1); // NOK
^
<source>:13:21: note: candidates are:
<source>:2:17: note: void func(const T&, R ...) [with X = int; T = S<int>; R = {int}]
inline void func(const T&, R...) {}
^
<source>:8:17: note: void func(const S<T>&, R ...) [with X = int; T = int; R = {int}]
inline void func(const S<T>&, R...) {}
^
<source>:14:26: error: call of overloaded 'func(S<int>, int)' is ambiguous
func<int>(S<int>(), 1); // NOK
^
...
Reproducible with gcc v4.8.1 and v9.1. Compiles with clang (v3.0.0 and v8.0.0), icc (v13.0.1 and v19.0.1), msvc (v19.14 and v19.20).
Is the code valid or is this a bug in gcc?
EDIT: Thanks everyone, your feedback was helpful for me. FYI, bug 90642 has been filed; looking forward for a definite answer.
Interesting question. I think what you run into here is overload resolution, more specifically partial ordering rules for template specialization
I quote:
Informally "A is more specialized than B" means "A accepts fewer types than B".
I think the clang is correct to compile that and the resulution should take the second candiate
template <typename X = int, typename T, typename ...R>
inline void func(const S<T>& t, R... p) {}
Because in case the first argument is not of type S<T>, it is no longer viable and thus more specialized.

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))

deduce of argument of type class method (overloads by const qualifier) fails with trailing return type in gcc, but not in clang

Nothing clearer than an old good MCVE:
struct X {
auto get(int) const -> int { return {}; }
auto get(int) -> int { return {}; }
};
template <class R> auto f(auto (X::*)(int) const -> R) {}
// ^~~~ ~~~~
// trailing return type
int main() {
f(&X::get);
}
This fails in g++ (4.9.2 & 5.1.0). However if the old return type is used:
template <class R> auto f(R (X::*)(int) const) {}
// ^
// old return type
it works.
On clang (3.5.0) both variants work.
I know that trailing return type changes when the return type is inferred and the scope of it, so I wouldn't be quick to cast it as a gcc bug. So what does the standard says? Which compiler is right?
The most significant message in the error I think is
couldn't deduce template parameter ‘R’`
g++ full message:
main2.cpp: In function ‘int main()’:
main2.cpp:21:12: error: no matching function for call to ‘f(<unresolved overloaded function type>)’
f(&X::get);
^
main2.cpp:18:25: note: candidate: template<class R, class auto:1> auto f(auto:1 (X::*)(int) const)
template <class R> auto f(auto (X::*)(int) const -> R) {}
^
main2.cpp:18:25: note: template argument deduction/substitution failed:
main2.cpp:21:12: note: types ‘auto:1 (X::)(int) const’ and ‘int (X::)(int)’ have incompatible cv-qualifiers
f(&X::get);
^
main2.cpp:21:12: note: couldn't deduce template parameter ‘R’
<builtin>: recipe for target 'main2' failed
make: *** [main2] Error 1
As pointed in the question this is a gcc bug which was beed fixed in version 6
gcc.gnu.org/bugzilla/show_bug.cgi?id=69139