How to extract requires clause with two parameter packs into a concept? - c++

I have this (supposedly not so useful) class template with templated constructor which is a candidate for perfect forwarding. I, however, wanted to make sure that the types passed to the constructor are the exact same as the ones specified for the whole class (without cvref qualifiers):
template <typename... Ts>
struct foo {
template <typename... CTs>
requires (std::same_as<std::remove_cvref_t<Ts>, std::remove_cvref_t<CTs>> && ...)
foo(CTs&& ...) {
std::cout << "called with " << (sizeof...(CTs)) << " args\n";
}
};
Now I can make:
auto f = foo<int, int>(1, 1);
and I can't make:
auto f = foo<float, int>(1, 1);
which is good.
But I wanted to extract the requires ... body to a concept:
template <typename... T1s, typename... T2s>
concept same_unqualified_types = (
std::same_as<
std::remove_cvref_t<T1s>,
std::remove_cvref_t<T2s>
> && ...
);
template <typename... Ts>
struct foo {
template <typename... CTs>
requires same_unqualified_types<Ts..., CTs...>
foo(CTs&& ...) { // 16
std::cout << "called with " << (sizeof...(CTs)) << " args\n";
}
};
int main() {
auto f = foo<int, int>(1, 1); // 22
}
But this gives me this error:
main.cpp: In function 'int main()':
main.cpp:22:32: error: no matching function for call to 'foo<int, int>::foo(int, int)'
22 | auto f = foo<int, int>(1, 1);
|
main.cpp:16:5: note: candidate: 'template<class ... CTs> requires same_unqualified_types<Ts ..., CTs ...> foo<Ts>::foo(CTs&& ...) [with CTs = {CTs ...}; Ts = {int, int}]'
16 | foo(CTs&& ...) {
| ^~~
main.cpp:16:5: note: template argument deduction/substitution failed:
main.cpp:16:5: note: constraints not satisfied
main.cpp: In substitution of 'template<class ... CTs> requires same_unqualified_types<Ts ..., CTs ...> foo<int, int>::foo(CTs&& ...) [with CTs = {int, int}]':
main.cpp:22:32: required from here
main.cpp:5:9: required for the satisfaction of 'same_unqualified_types<Ts ..., CTs ...>' [with CTs = {int, int}; Ts = {int, int}]
main.cpp:22:32: error: mismatched argument pack lengths while expanding 'same_as<typename std::remove_cvref<T1s>::type, typename std::remove_cvref<T2s>::type>'
22 | auto f = foo<int, int>(1, 1);
|
I suppose I might be doing something wrong with the concept same_unqualified_types where I'm trying to have two parameter packs. I've tried to test it manually, but it doesn't seem to work, even if I do same_unqualified_types<int, int> or same_unqualified_types<int, int, Pack...>, where Pack... is a parameter pack of two ints.
Where is my logic flawed? Can I extract that requires clause to a concept?
Disclaimer: I know I can achieve something similar with CTAD and deduction guides - without even needing any concepts. I just wish to know where my understanding is flawed.

Concepts don't get special privileges with regard to template parameter packs. You can't have two packs in a set of template parameters unless there's something to distinguish them in terms of the arguments passed to them (one could be a series of types while the other is a series of values, or you have access to template argument deduction to differentiate them, or something like that).
The best you're going to be able to do is to create an unqualified equivalent of same_as and just use that with pack expansion as needed:
template<typename T, typename U>
concept same_as_unqual = std::same_as<<std::remove_cvref_t<T>, <std::remove_cvref_t<U>>;
template <typename... Ts>
struct foo {
template <typename... CTs>
requires (same_as_unqual<Ts, CTs> && ...)
...
A single concept that does the pair-wise comparison between the parameters just isn't possible. Well, it's possible, but it'd be a lot more verbose, as you would likely need to bundle them in some kind of type-list. I don't think same_as_all<type_list<Ts...>, type_list<Us...>> is better than doing an explicit expansion.

Related

well-known overloads for std::visit does not work with reference_wrapper

Here's a sample code: http://coliru.stacked-crooked.com/a/5f630d2d65cd983e
#include <variant>
#include <functional>
template<class... Ts> struct overloads : Ts... { using Ts::operator()...; };
template<class... Ts> overloads(Ts &&...) -> overloads<std::remove_cvref_t<Ts>...>;
template<typename... Ts, typename... Fs>
constexpr inline auto transform(const std::variant<Ts...> &var, Fs &&... fs)
-> decltype(auto) { return std::visit(overloads{fs...}, var); }
template<typename... Ts, typename... Fs>
constexpr inline auto transform_by_ref(const std::variant<Ts...> &var, Fs &&... fs)
-> decltype(auto) { return std::visit(overloads{std::ref(fs)...}, var); }
int main()
{
transform(
std::variant<int, double>{1.0},
[](int) { return 1; },
[](double) { return 2; }); // fine
transform_by_ref(
std::variant<int, double>{1.0},
[](int) { return 1; },
[](double) { return 2; }); // compilation error
return 0;
}
Here, I have adopted the well-known overloads helper type to invoke std::visit() with multiple lambdas.
transform() copies function objects so I write a new function transform_by_ref() which utilizes std::reference_wrapper to prevent copying function objects.
Even though original lambdas are temporary objects, the lifetime is ensured at the end of execution of transform_by_ref() and I think lifetime should not be a problem here.
transform() works as expected but transform_by_ref() causes compilation error:
main.cpp: In instantiation of 'constexpr decltype(auto) transform_by_ref(const std::variant<_Types ...>&, Fs&& ...) [with Ts = {int, double}; Fs = {main()::<lambda(int)>, main()::<lambda(double)>}]':
main.cpp:18:21: required from here
main.cpp:13:42: error: no matching function for call to 'visit(overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >, const std::variant<int, double>&)'
13 | -> decltype(auto) { return std::visit(overloads{std::ref(fs)...}, var); }
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from main.cpp:1:
/usr/local/include/c++/12.1.0/variant:1819:5: note: candidate: 'template<class _Visitor, class ... _Variants> constexpr std::__detail::__variant::__visit_result_t<_Visitor, _Variants ...> std::visit(_Visitor&&, _Variants&& ...)'
1819 | visit(_Visitor&& __visitor, _Variants&&... __variants)
| ^~~~~
/usr/local/include/c++/12.1.0/variant:1819:5: note: template argument deduction/substitution failed:
In file included from /usr/local/include/c++/12.1.0/variant:37:
/usr/local/include/c++/12.1.0/type_traits: In substitution of 'template<class _Fn, class ... _Args> using invoke_result_t = typename std::invoke_result::type [with _Fn = overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >; _Args = {const int&}]':
/usr/local/include/c++/12.1.0/variant:1093:11: required by substitution of 'template<class _Visitor, class ... _Variants> using __visit_result_t = std::invoke_result_t<_Visitor, std::__detail::__variant::__get_t<0, _Variants, decltype (std::__detail::__variant::__as(declval<_Variants>())), typename std::variant_alternative<0, typename std::remove_reference<decltype (std::__detail::__variant::__as(declval<_Variants>()))>::type>::type>...> [with _Visitor = overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >; _Variants = {const std::variant<int, double>&}]'
/usr/local/include/c++/12.1.0/variant:1819:5: required by substitution of 'template<class _Visitor, class ... _Variants> constexpr std::__detail::__variant::__visit_result_t<_Visitor, _Variants ...> std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >; _Variants = {const std::variant<int, double>&}]'
main.cpp:13:42: required from 'constexpr decltype(auto) transform_by_ref(const std::variant<_Types ...>&, Fs&& ...) [with Ts = {int, double}; Fs = {main()::<lambda(int)>, main()::<lambda(double)>}]'
main.cpp:18:21: required from here
/usr/local/include/c++/12.1.0/type_traits:3034:11: error: no type named 'type' in 'struct std::invoke_result<overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >, const int&>'
3034 | using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
| ^~~~~~~~~~~~~~~
main.cpp: In instantiation of 'constexpr decltype(auto) transform_by_ref(const std::variant<_Types ...>&, Fs&& ...) [with Ts = {int, double}; Fs = {main()::<lambda(int)>, main()::<lambda(double)>}]':
main.cpp:18:21: required from here
/usr/local/include/c++/12.1.0/variant:1859:5: note: candidate: 'template<class _Res, class _Visitor, class ... _Variants> constexpr _Res std::visit(_Visitor&&, _Variants&& ...)'
1859 | visit(_Visitor&& __visitor, _Variants&&... __variants)
| ^~~~~
/usr/local/include/c++/12.1.0/variant:1859:5: note: template argument deduction/substitution failed:
main.cpp:13:42: note: couldn't deduce template parameter '_Res'
13 | -> decltype(auto) { return std::visit(overloads{std::ref(fs)...}, var); }
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I think I can fix this by not using std::visit() and implement my own visit function anyway.
However, I want to know why this code does not work as expected.
Why does my transform_by_ref() cause compilation error and how to fix it without custom visit function implementation?
Each std::reference_wrapper has an operator() overload that can be called with any argument list that the referenced lambda would accept as arguments.
That means the reference wrappers for both [](int) { return 1; } and [](double) { return 2; } have operator() overloads that accept an int argument as well as an double argument, both without conversion of the argument.
So when std::visit tries to do overload resolution for a specific element type of the variant, the operator() overloads made visible via using Ts::operator()...; for both reference wrappers of the lambdas will be viable, but in contrast to the non-reference-wrapper case, both overloads will be viable without conversions of the argument, meaning that they are equally good and hence overload resolution ambiguous.
The ambiguity can be avoided by enforcing that the lambdas take only exactly the type they are supposed to match as argument (assuming C++20 here):
transform_by_ref(
std::variant<int, double>{1.0},
[](std::same_as<int> auto) { return 1; },
[](std::same_as<double> auto) { return 2; });
or by using a single overload with if constexpr in its body to branch on the type of the argument.
While it is possible to make the operator() of a wrapper class SFINAE-friendly so that it won't be considered viable if the wrapped callable isn't, it is impossible to "forward" the conversion rank of calls for calls to such a wrapper, at least in general. For non-generic lambdas specifically, it is theoretically possible to extract the parameter type in the wrapper and use it as the parameter type of the operator() overload, but that is messy and doesn't work with generic callables. Proper reflection would be required to implement such a wrapper.
In your code for transform you are using fs directly as lvalue instead of properly forwarding its value category via std::forward<Fs>(fs). If you used that instead, then only move construction would be used, instead of copies.
If the goal is to also avoid the move construction, the usual approach which constructs overloads in the caller already achieves that:
template<typename... Ts, typename Fs>
constexpr inline auto transform(const std::variant<Ts...> &var, Fs && fs)
-> decltype(auto) { return std::visit(std::forward<Fs>(fs), var); }
int main()
{
transform(
std::variant<int, double>{1.0},
overloads{
[](int) { return 1; },
[](double) { return 2; }});
return 0;
}
This uses aggregate-initialization of overloads from prvalues, which means mandatory copy elision applies and no lambdas will be copied or moved.
The std::ref approach, even if it did work, would also waste memory to store the references for non-capturing lambdas.

Template deduction, is this conforming?

It seems like this code is not correct as I get compiler errors for it. I'm trying to understand why:
template <class ... Ts>
struct type_list{};
template <class ... Ts, class T_, class ... Ts_>
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
return sizeof(T_);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
return 0;
}
clang produces the follow error:
example.cpp:16:16: error: no matching function for call to 'foo'
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
^~~
example.cpp:11:6: note: candidate template ignored: deduced conflicting types for parameter 'Ts'
(<int> vs. <>)
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
It seems to me that in my call Ts should be deduced to int (since that's the only way to make the first argument work out), and then everything else will be forced as a result. Why doesn't this work?
Clang deduces Ts from two conflicting sources: type_list<Ts...> and Ts&&.... Because you call foo with type_list<int>{}, Ts is first deduced as {int}. However, because the last argument Ts_&&... has the type of a parameter pack, it catches all that it can; in this case, the last two arguments (while T_&&t is passed the 5), which deduces Ts_ as {double, int} (and T_ as int). This leaves Ts with no argument, so it's deduced as {}.
Hence Clang's error message: Ts is deduced both as {} and {int} (the (<int> vs. <>) in the error message).
Note that GCC does things differently:
error: too few arguments to function 'auto foo(type_list, Ts&& ..., T_&&, Ts_&& ...) [with Ts = {int}; T_ = int; Ts_ = {double, int}]'
It tries to keep Ts deduced as {int} but still deduces Ts_ as {double, int}, and so there is no way to give the correct number of arguments.
To explain a bit further, consider the following:
template <class ... Ts>
struct type_list{};
template <class ... Ts>
auto foo(type_list<Ts...>, Ts...) {
return sizeof...(Ts);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
}
Clang correctly calls this out because the two deductions are conflicting:
note: candidate template ignored: deduced conflicting types for parameter 'Ts' (<int> vs. <int, double, int>)
Trouble is, you can't catch 2 of the arguments with another parameter pack, because it will either catch everything if placed after (which is your case), or nothing if placed before:
template <class ... Ts>
struct type_list{};
template <class... Ts, class... Ts_>
auto foo(type_list<Ts...>, Ts_..., Ts...) {
return sizeof...(Ts);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
}
Here, Clang produces the same error message because Ts is still deduced as both {int} from the type_list, and {int, double, int} by catching all the remaining arguments.
I think your only choices are to somehow deal with tuples or be more explicit with the call: foo<int>(type_list<int>{}, 5, 5.0, 3) (and you can probably remove the type_list in this case). That way, Ts isn't deduced anymore: you explicitly make it {int}.
C++ doesn't try every possibility and find the only one that is legal. It follows very specific rules, and if those rules don't work, it generates an error.
In general, a deduced ... pack must be last if it is deduced. Attempts to work around it will fail.
Here is a solution that avoids your problem:
template <class ... Ts, class...Us>
auto foo(type_list<Ts...>, Us&&...us) {
return [](Ts&&...ts, auto&& t, auto&&...) {
return sizeof(t);
}(std::forward<Us>(us)...);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
return 0;
}

C++ Templates: correct way to return a new type

Sorry for the generic title, but I'm unable to focus the problem.
I have a templatized class method that accept an argument pack and provides a new type in return, to hide the details of the implementation. More specifically, the class handles SQLite queries, and the method calls sqlite3_prepare() to prepare the statement before executing the query.
class Table {
...
template <typename ...Ts>
class PreparedStatement { ... };
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( ... );
}
That works well with "normal" types, but the problem occurs when the arguments are declared const:
const Field<int> fld = createField<int>("name");
...
PreparedStatement<decltype(fld)> s = prepare(make_tuple(fld));
The error is the following:
no match for 'operator =' (operand types are PreparedStatenent<const Field<int>> and PreparedStatement<Field<int>>
I suspect the issue is in my declaration of the function, is there a way to fix this issue and make the function more "elegant" ?
NOTE: I know I can fix the issue by manually declare the s variable, but my doubts are on how the method was implemented.
As Many Asked, here's an example:
#include <tuple>
template <typename T>
struct Field {
};
class Table {
public:
template <typename ...Ts>
class PreparedStatement {
public:
PreparedStatement() {};
};
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( );
}
};
int main()
{
Field<int> fld;
Table t;
Table::PreparedStatement<decltype(fld)> p;
p = t.prepare(std::make_tuple(fld));
// here comes the problem
const Field<int> f2;
Table::PreparedStatement<decltype(f2)> p2;
p2 = t.prepare(std::make_tuple(f2));
return 0;
}
and here's the compiler output
main.cpp: In function 'int main()': main.cpp:35:39: error: no match
for 'operator=' (operand types are 'Table::PreparedStatement >' and 'Table::PreparedStatement >')
p2 = t.prepare(std::make_tuple(f2));
^ main.cpp:10:10: note: candidate: constexpr Table::PreparedStatement >&
Table::PreparedStatement >::operator=(const
Table::PreparedStatement >&)
class PreparedStatement {
^~~~~~~~~~~~~~~~~ main.cpp:10:10: note: no known conversion for argument 1 from 'Table::PreparedStatement >'
to 'const Table::PreparedStatement >&'
main.cpp:10:10: note: candidate: constexpr
Table::PreparedStatement >&
Table::PreparedStatement
::operator=(Table::PreparedStatement >&&) main.cpp:10:10: note: no known conversion for argument 1 from
'Table::PreparedStatement >' to
'Table::PreparedStatement >&&'
UPDATE
As many noted, I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context.
So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
cppreference.com for make_tuple tells us:
template< class... Types >
tuple<VTypes...> make_tuple( Types&&... args );
For each Ti in Types..., the corresponding type Vi in Vtypes... is
std::decay<Ti>::type unless application of std::decay results in
std::reference_wrapper<X> for some type X, in which case the deduced
type is X&.
While std::decay, among other things, removes cv-qualifiers. So your type will be no PreparedStatement<const Field<int>>, but PreparedStatement<Field<int>>.
You can use auto, as manni66 proposed, to avoid such problems.
auto s = prepare(make_tuple(fld));
I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context. So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
Instead of auto, you can use a decltype expression that take in count the value returned by prepare.
I mean... instead of
Table::PreparedStatement<decltype(f2)> p2;
you can try with
decltype(t.prepare(std::make_tuple(f2))) p2;
or
decltype(std::declval<Table>().prepare(
std::make_tuple(std::declval<Field<int>>()))) p2;
I suppose you can use a similar decltype() also to declare members of your classes.

Failed to compile because of incompatible cv-qualifiers

I have two template method
template <typename T, typename Ret, typename ...Args>
Ret apply(T* object, Ret(T::*method)(Args...), Args&& ...args) {
return (object->*method)(std::forward(args)...);
};
template <typename T, typename Ret, typename ...Args>
Ret apply(T* object, Ret(T::*method)(Args...) const, Args&& ...args) {
return (object->*method)(std::forward(args)...);
};
My purpose is apply member method of class T on these args
this is my test code:
int main() {
using map_type = std::map<std::string, int>;
map_type map;
map.insert(std::make_pair("a", 1));
std::cout << "Map size: " << apply(&map, &map_type::size) << std::endl; //this code work
apply(&map, &map_type::insert, std::make_pair("a", 1)); //failed to compile
return 0;
}
This is compiler error message:
test.cpp: In function ‘int main()’:
test.cpp:61:58: error: no matching function for call to ‘apply(map_type*, <unresolved overloaded function type>, std::pair<const char*, int>)’
apply(&map, &map_type::insert, std::make_pair("a", 1));
^
test.cpp:11:5: note: candidate: template<class T, class Ret, class ... Args> Ret apply(T*, Ret (T::*)(Args ...), Args&& ...)
Ret apply(T* object, Ret(T::*method)(Args...), Args&& ...args) {
^~~~~
test.cpp:11:5: note: template argument deduction/substitution failed:
test.cpp:61:58: note: couldn't deduce template parameter ‘Ret’
apply(&map, &map_type::insert, std::make_pair("a", 1));
std::map::insert is an overloaded function. You cannot take its address unless you explicitly specify the overload you're interested about - how else would the compiler know?
The easiest way to solve your problem is to have apply accept an arbitrary function object and wrap your call to insert in a generic lambda.
template <typename F, typename ...Args>
decltype(auto) apply(F f, Args&& ...args) {
return f(std::forward<Args>(args)...);
};
Usage:
::apply([&](auto&&... xs) -> decltype(auto)
{
return map.insert(std::forward<decltype(xs)>(xs)...);
}, std::make_pair("a", 1));
live wandbox example
The additional syntactic boilerplate is unfortunately impossible to avoid. This might change in the future, see:
N3617 aimed to solve this issue by introducing a "lift" operator.
P0119 by A. Sutton solves the problem in a different way by allowing overload sets to basically generate the "wrapper lambda" for you when passed as arguments.
I'm not sure if overloaded member functions are supported in the above proposals though.
You can alternatively use your original solution by explicitly specifying the overload you're intersted in on the caller side:
::apply<map_type, std::pair<typename map_type::iterator, bool>,
std::pair<const char* const, int>>(
&map, &map_type::insert<std::pair<const char* const, int>>,
std::make_pair("a", 1));
As you can see it's not very pretty. It can be probably improved with some better template argument deduction, but not by much.

gcc 4.7 about Variadic Templates/ decltype /std::forward

char foo()
{
std::cout<<"foo()"<<std::endl;
return 'c';
}
void foo(char &&i)
{
std::cout<<"foo(char &&i)"<<std::endl;
}
struct pipe {};
template<class OP>
struct Flow;
template<>
struct Flow<pipe> {
template<class L,class R>
static auto apply(L&& l,R &&r)->decltype(r(std::forward<L>(l))) {
return r(std::forward<L>(l));
}
};
template<class L,class R,class E>
struct Pipe;
template<class F,class...ARGS>
auto eval(F& f,ARGS&&... arg)->decltype(f(std::forward<ARGS>(arg)...))
{
return f(std::forward<ARGS>(arg)...);
}
template<class L,class R,class E,class...ARGS>
auto eval(Pipe<L,R,E>&f,ARGS&&... arg)->decltype(Flow<E>::apply(eval(f.lhs,std::forward<ARGS>(arg)...),f.rhs))
{
return Flow<E>::apply(eval(f.lhs,std::forward<ARGS>(arg)...),f.rhs);
}
template<class L,class R,class E>
struct Pipe {
L lhs;
R rhs;
Pipe(L &l,R& r):lhs(l),rhs(r) {
}
template<class...ARGS>
auto operator()(ARGS&&... arg)->decltype(eval<L,R,E >(*this,std::forward<ARGS>(arg)...)) {
return eval<L,R,E >(*this,std::forward<ARGS>(arg)...);
}
};
void streamtest()
{
void (*foo1)(char &&)=foo;
void (*foo2)(int ,int ,short )=foo;
char (*foo3)()=foo;
Pipe<char(*)(),void(*)(char&&),pipe> pp(foo3,foo1);
pp(1);
}
I want write a pipe Library for function transfer. but error Let me confused:
\FEstream.cpp: In function 'void streamtest()':
\FEstream.cpp:117:9: error: no match for call to '(Pipe<char (*)(), void (*)(char&&), pipe>) (int)'
\FEstream.cpp:98:8: note: candidate is:
\FEstream.cpp:104:13: note: template<class ... ARGS> decltype (eval<L, R, E>((* this), (forward<ARGS>)(Pipe::operator()::arg)...)) Pipe::operator()(ARGS&& ...) [with ARGS = {ARGS ...}; L = char (*)(); R = void (*)(char&&); E = pipe]
\FEstream.cpp:104:13: note: template argument deduction/substitution failed:
\FEstream.cpp: In substitution of 'template<class ... ARGS> decltype (eval<L, R, E>((* this), (forward<ARGS>)(Pipe::operator()::arg)...)) Pipe::operator()(ARGS&& ...) [with ARGS = {ARGS ...}; L = char (*)(); R = void (*)(char&&); E = pipe] [with ARGS = {int}]':
\FEstream.cpp:117:9: required from here
\FEstream.cpp:104:13: error: no matching function for call to 'eval(Pipe<char (*)(), void (*)(char&&), pipe>&, int)'
\FEstream.cpp:104:13: note: candidates are:
\FEstream.cpp:88:6: note: template<class F, class ... ARGS> decltype (f((forward<ARGS>)(eval::arg)...)) eval(F&, ARGS&& ...)
\FEstream.cpp:88:6: note: template argument deduction/substitution failed:
\FEstream.cpp:104:13: note: cannot convert '*(Pipe<char (*)(), void (*)(char&&), pipe>*)this' (type 'Pipe<char (*)(), void (*)(char&&), pipe>') to type 'char (*&)()'
\FEstream.cpp:93:6: note: template<class L, class R, class E, class ... ARGS> decltype (Flow<E>::apply(eval(f.lhs, (forward<ARGS>)(eval::arg)...), f.rhs)) eval(Pipe<L, R, E>&, ARGS&& ...)
\FEstream.cpp:93:6: note: template argument deduction/substitution failed:
\FEstream.cpp: In substitution of 'template<class L, class R, class E, class ... ARGS> decltype (Flow<E>::apply(eval(f.lhs, (forward<ARGS>)(arg)...), f.rhs)) eval(Pipe<L, R, E>&, ARGS&& ...) [with L = char (*)(); R = void (*)(char&&); E = pipe; ARGS = {int}]':
\FEstream.cpp:104:13: required by substitution of 'template<class ... ARGS> decltype (eval<L, R, E>((* this), (forward<ARGS>)(Pipe::operator()::arg)...)) Pipe::operator()(ARGS&& ...) [with ARGS = {ARGS ...}; L = char (*)(); R = void (*)(char&&); E = pipe] [with ARGS = {int}]'
\FEstream.cpp:117:9: required from here
\FEstream.cpp:93:6: error: no matching function for call to 'eval(char (*&)(), int)'
\FEstream.cpp:93:6: note: candidate is:
\FEstream.cpp:88:6: note: template<class F, class ... ARGS> decltype (f((forward<ARGS>)(eval::arg)...)) eval(F&, ARGS&& ...)
\FEstream.cpp:88:6: note: template argument deduction/substitution failed:
\FEstream.cpp: In substitution of 'template<class F, class ... ARGS> decltype (f((forward<ARGS>)(arg)...)) eval(F&, ARGS&& ...) [with F = char (*)(); ARGS = {int}]':
\FEstream.cpp:93:6: required by substitution of 'template<class L, class R, class E, class ... ARGS> decltype (Flow<E>::apply(eval(f.lhs, (forward<ARGS>)(eval::arg)...), f.rhs)) eval(Pipe<L, R, E>&, ARGS&& ...) [with L = char (*)(); R = void (*)(char&&); E = pipe; ARGS = {int}]'
\FEstream.cpp:104:13: required by substitution of 'template<class ... ARGS> decltype (eval<L, R, E>((* this), (forward<ARGS>)(Pipe::operator()::arg)...)) Pipe::operator()(ARGS&& ...) [with ARGS = {ARGS ...}; L = char (*)(); R = void (*)(char&&); E = pipe] [with ARGS = {int}]'
\FEstream.cpp:117:9: required from here
\FEstream.cpp:88:6: error: too many arguments to function
Process terminated with status 1 (0 minutes, 0 seconds)
what's happening?Is it my error,or gcc's not C++11 compliant?
////////////////////////////////////////////////////////////////////////////////////////////
thanks Dave S.but ,code is only simplification.In fact, I use templateEval::eval:
template<class L,class R,class E>
struct Pipe;
template<class F>
struct Eval {
template<class...ARGS>
static auto eval(F&f,ARGS&&... arg)->decltype(f(std::forward<ARGS>(arg)...)) {
return f(std::forward<ARGS>(arg)...);
}
};
template<class L,class R,class E>
struct Eval<Pipe<L,R,E> > {
static auto eval(Pipe<L,R,E>&f)->decltype(Flow<E>::apply(f.lhs,f.rhs)) {
return Flow<E>::apply(f.lhs,f.rhs);
}
template<class...ARGS>
static void eval(Pipe<L,R,E>&f,ARGS&&...arg) {
static_assert(!std::is_same<E,pipe>::value,
"multiple input for expression\nsample: auto expr=wrap(foo1)<var1|foo2 ;call expr(var2) instead of expr()");
}
};
template<class L,class R>
struct Eval<Pipe<L,R,pipe> > {
template<class...ARGS>
static auto eval(Pipe<L,R,pipe>&f,ARGS&&... arg)->decltype(Flow<pipe>::apply(Eval<L>::eval(f.lhs,std::forward<ARGS>(arg)...),f.rhs)) {
return Flow<pipe>::apply(Eval<L>::eval(f.lhs,std::forward<ARGS>(arg)...),f.rhs);
}
};
template<class L,class R,class E>
struct Pipe {
L lhs;
R rhs;
Pipe(L &l,R& r):lhs(l),rhs(r) {
}
template<class...ARGS>
auto operator()(ARGS&&... arg)->decltype(Eval<Pipe>::eval(*this,std::forward<ARGS>(arg)...)) {
return Eval<Pipe>::eval(*this,std::forward<ARGS>(arg)...);
}
};
void streamtest()
{
void (*foo1)(char &&)=foo;
void (*foo2)(int ,int ,short )=foo;
char (*foo3)()=foo;
Pipe<char(*)(),void(*)(char&&),pipe> pp(foo3,foo1);
//pp(); //no call!
}
error is:
FEstream.cpp: In instantiation of 'struct Eval >':
FEstream.cpp:121:9: required from 'struct Pipe'
FEstream.cpp:134:45: required from here
FEstream.cpp:110:18: error: invalid use of incomplete type
'struct Pipe'
FEstream.cpp:115:8: error: declaration of 'struct Pipe
void (*)(char&&), pipe>'
FEstream.cpp:110:18: error: invalid use of incomplete type
'struct Pipe'
FEstream.cpp:115:8: error: declaration of 'struct Pipe
void (*)(char&&), pipe>'
Process terminated with status 1 (0 minutes,
0 seconds) 6 errors, 0 warnings
Pipe::operator()(ARGS&&... arg) is a template member function.why I declaring variable Pipe(pp) Cause an error? it Should not be instantiated because I have not used itenter code here
anybody?
and I forget a status when eval function use by Pipe like
template<class...ARGS>
auto operator()(ARGS&&... arg)->decltype(eval(*this,std::forward<ARGS>(arg)...)) {
return eval(*this,std::forward<ARGS>(arg)...);
}
not
template<class...ARGS>
auto operator()(ARGS&&... arg)->decltype(eval<L,R,E>(*this,std::forward<ARGS>(arg)...)) {
return eval<L,R,E>(*this,std::forward<ARGS>(arg)...);
}
will error like reece:
template instantiation depth exceeds maximum of 900 .....
Seems to be select Eval(F&.... instead of eval(Pipe&f..... when not specify a template parameter
It's having trouble due to an argument mismatch, somewhere in your call chain. So, we can do it manually to find the problem.
Pipe<char(*)(),void(*)(char&&),pipe> pp(foo3,foo1); is using foo3, which takes 0 arguments as its L, and foo1, which takes an char rvalue-reference as R. And E is your marker structure pipe
When invoked with the int 1.
pp(1) calls eval<L,R,E>(*this, 1), which in turn calls
Flow<E>::apply(eval(foo3,1),foo1).
First, the inner eval is called. This attempts to determine the declval of foo3(1), however, foo3 was declared to take 0 arguments. This causes a compilation failure, which results in the substitution failures you're getting.
Edit: With the changed question, your problem is now you're creating a specialization of Eval for Pipe, but Eval is attempting to use fields of Pipe in it's return declaration (via decltype), and Pipe is doing the same. You're going to have to break that cycle so something can be defined first, or at least set it up so that the cycle isn't introduced in the function declaration, so you can define the methods after you've fully defined both types.
I'm not sure what the Eval class is attempting to accomplish. One solution might be to remove that altogether and simply have Pipe::operator() invoke the method more directly.
I'm building this on Ubuntu with gcc 4.6 (I don't have a version of gcc 4.7 to try) so YMMV.
gcc 4.6 : g++-4.6 -std=c++0x test.cpp
void (*foo2)(int ,int ,short )=foo; -- there is no version of foo matching this signature, so I commented it out.
error: expected a type, got ‘pipe’ -- pipe appears to be defined elsewhere, so renamed it to pipe_.
error: invalid use of ‘this’ at top level -- gcc 4.6 does not like auto operator()->decltype(*this) syntax, so replaced *this with Pipe<L,R,E>(lhs,rhs).
error: no match for call to ‘(Pipe<char (*)(), void (*)(char&&), pipe_>) (int)’ -- gcc 4.6 is failing to match the operator(). Here is where I am puzzled.
clang 3.1 : clang -std=c++11 test.cpp
same mismatched foo declaration as gcc
same "expected 'pipe' to be a type" error as gcc
error: no matching function for call to object of type 'Pipe<char (*)(), void (*)(char &&), pipe_>' when calling operator()
Ok. Both gcc and clang indicate an issue with the operator() definition.
template<class...ARGS>
auto operator()(ARGS&&... arg)->decltype(eval<L,R,E >(*this,std::forward<ARGS>(arg)...));
Here you are calling eval with *this and the forwarded arguments. There are two versions of eval:
template<class F,class...ARGS>
auto eval(F& f,ARGS&&... arg)->decltype(f(std::forward<ARGS>(arg)...));
and:
template<class L,class R,class E,class...ARGS>
auto eval(Pipe<L,R,E>&f,ARGS&&... arg)->decltype(Flow<E>::apply(eval(f.lhs,std::forward<ARGS>(arg)...),f.rhs));
Now, because eval is a function and all arguments are specified in its arguments, you don't need to specify them explicitly. Doing so like:
eval<L,R,E >(*this,std::forward<ARGS>(arg)...)
is telling the compiler that the first argument is L which it is not, it is Pipe<L,R,E>.
Changing the operator() definition to:
template<class...ARGS>
auto operator()(ARGS&&... arg)->decltype(eval(*this,std::forward<ARGS>(arg)...));
now crashes clang and gcc!
EDIT: Ok, now trying the new version with gcc 4.7 I now get:
test.cpp:30:10: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) substituting ‘template<class _Tp> constexpr _Tp&& std::forward(typename std::remove_reference<_Tp>::type&&) [with _Tp = int]’
test.cpp:30:10: required by substitution of ‘template<class L, class R, class E, class ... ARGS> decltype (Flow<E>::apply(eval(f.lhs, (forward<ARGS>)(arg)...), f.rhs)) eval(Pipe<L, R, E>&, ARGS&& ...) [with L = char (*)(); R = void (*)(char&&); E = pipe_; ARGS = int]’
test.cpp:41:17: required by substitution of ‘template<class ... ARGS> decltype (eval(Pipe(((Pipe*)this)->Pipe<L, R, E>::lhs, ((Pipe*)this)->Pipe<L, R, E>::rhs), (forward<ARGS>)(Pipe::operator()::arg)...)) Pipe::operator()(ARGS&& ...) [with ARGS = {ARGS ...}; L = char (*)(); R = void (*)(char&&); E = pipe_] [with ARGS = {int}]’
test.cpp:25:10: required by substitution of ‘template<class F, class ... ARGS> decltype (f((forward<ARGS>)(eval::arg)...)) eval(F&, ARGS&& ...) [with F = Pipe<char (*)(), void (*)(char&&), pipe_>; ARGS = {int}]’
with the recursion between 41:17 (Pipe<L,R,E>::operator()) and 25:10 (eval<F,ARGS>()), so it is not picking up the Pipe specialization of eval. Now I am stuck again.