C++ variadic templates - c++

I'm using a vector definition:
vector<lmlink_out> os{
lmlink_out(typeid(string &)),
lmlink_out(),
lmlink_out(typeid(int),typeid(char))
};
using the following code works (defined inside the class):
class lmlink_out {
vector<const type_info*> parameters;
...
public:
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out(const T&... args) : parameters({&args...}) {}
using this other leads to compilation error (defined outside the class):
class lmlink_out {
vector<const type_info*> parameters;
...
public:
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out(const T&... args);
};
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out::lmlink_out(const T&... args)
: parameters({(&args...})
{
}
the compiler (gcc version 10.2.1 20210110 (Debian 10.2.1-6)) returns:
error: no matching function for call to ‘std::vector<const
std::type_info*>::vector()’ 374 |
: parameters({(&args...}) note: candidate: ‘std::vector<_Tp,
_Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = const std::type_info*; _Alloc = std::allocator<const
std::type_info*>; std::vector<_Tp, _Alloc>::allocator_type =
std::allocator<const std::type_info*>]’ 625 |
vector(initializer_list<value_type> __l,
...
| ^~~~~~ /usr/include/c++/10/bits/stl_vector.h:625:43: note: no known conversion for argument 1 from ‘’ to ‘std::initializer_list<const std::type_info*>’
I think that two codes are semantically equivalent, why does the second give an error?
I need to add another constructor:
template<typename... T, std::size_t N = sizeof...(T)>
lmlink_out(const string &name, const T&... args) : name(name), parameters({&args...}) {}
but it lead to the same error above.

Your code has two simple problems:
You can't redefine template default arguments, i.e., you'll need to drop the = sizeof...(T) rom the definition of the ctor. It can only be present in the first declraation.
There is an excess '(' when calling the parameters ctor. It should be
: parameters({&args...})
This Compiler Explorer link shows the code compiling.

Related

How to build a parameter pack without any input values, from variadric class template?

I have a class that will produce a set of values and pass them to callback, as defined by variadic template arguments:
template <typename... TResults>
class Statement
{
public:
using HandleValues = std::function<bool(TResults...)>;
void getValues(const HandleValues& hander);
}
My real code is templated SQL statement handler, so these are the types to be read from a database. For simplification, just imagine the values produced by these dummy methods:
// Some overloads exist that produce a value:
template <typename TRead>
struct Produce
{
static TRead value() {}
};
template <>
struct Produce<int>
{
static int value() { return 42; }
};
template <>
struct Produce<bool>
{
static bool value() { return true; }
};
In real code, these are conversion traits.
The problematic code is trying to put the produced values in the callback. I have tried this:
template <typename... TResults>
void Statement<TResults...>::getValues(const RowHandler& handler)
{
return handler((Produce<TResults>::value(), ...));
}
The error I get:
MSVC: error C2064: term does not evaluate to a function taking 1 arguments
GCC is more verbose:
./include/sqlite3++/Statement.h:84:3: required from 'void sqlitepp::Statement<TResults>::execute(const RowHandler&, TValRest ...) [with TValRest = {const char*}; TResults = {double, int}; sqlitepp::Statement<TResults>::RowHandler = std::function<bool(double, int)>]'
../build/examples/basic/BasicSQLite/BasicSQLite.cpp:52:5: required from here
../include/sqlite3++/Statement.h:94:19: error: no match for call to '(const RowHandler {aka const std::function<bool(double, int)>}) (int)'
94 | return handler((ReadTraits<TResults>::ReadFromStatement(reader), ...));
| ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from c:\mingw\lib\gcc\mingw32\9.2.0\include\c++\functional:59,
from ../include/sqlite3++/internal/RawStatement.h:14,
from ../include/sqlite3++/Statement.h:5,
from ../build/examples/basic/BasicSQLite/BasicSQLite.cpp:6:
c:\mingw\lib\gcc\mingw32\9.2.0\include\c++\bits\std_function.h:685:5: note: candidate: '_Res std::function<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = bool; _ArgTypes = {double, int}]'
685 | function<_Res(_ArgTypes...)>::
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
Clearly, the arguments are not actually expanding. I tried other variants, such as also putting ... after the template argument, but nothing has helped.
Use this return handler(Produce::value()...);

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.

compile error when trying to use pointer-to-member function as projection to ranges::find()

I want to search an input range for an element that has a certain value in a member via an accessor.
range-v3 documentation is... thin, but there are sources such as this answer by AFAIK the 2 main range-v3 developers indicates this kind of thing should Just Work, albeit with sort not find.
Given the code, compiled with g++ -std=c++17 against ericniebler/range-v3 release range-v3-0.10.0, using g++.exe (Rev2, Built by MSYS2 project) 9.3.0:
#include <range/v3/algorithm/find.hpp>
#include <vector>
auto
main() -> int
{
struct S {
int i{};
auto get_i() const { return i; }
};
auto const ss = std::vector<S>(10);
ranges::find(ss, 1, &S::get_i);
return 0;
}
I get a spew of errors:
test.cpp: In function 'int main()':
test.cpp:14:31: error: no match for call to '(const ranges::find_fn) (const std::vector<main()::S>&, int, int (main()::S::*)() const)'
14 | ranges::find(ss, 1, &S::get_i);
| ^
In file included from FOO/include/range-v3/range/v3/range_fwd.hpp:24,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:18,
from test.cpp:1:
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: candidate: 'template<class I, class S, class V, class P> constexpr concepts::return_t<I, typename std::enable_if<(((input_iterator<I> && sentinel_for<S, I>) && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P>::apply<I>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(I, S, const V&, P) const'
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:47:24: note: in expansion of macro 'RANGES_FUNC'
47 | constexpr auto RANGES_FUNC(find)(I first, S last, V const & val, P proj = P{})
| ^~~~~~~~~~~
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: template argument deduction/substitution failed:
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:47:24: note: in expansion of macro 'RANGES_FUNC'
47 | constexpr auto RANGES_FUNC(find)(I first, S last, V const & val, P proj = P{})
| ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/iterator/access.hpp:22,
from FOO/include/range-v3/range/v3/iterator/concepts.hpp:30,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:22,
from test.cpp:1:
FOO/include/range-v3/std/detail/associated_types.hpp: In substitution of 'template<bool B, class T> using enable_if_t = typename ranges::detail::enable_if::apply<T> [with bool B = ranges::readable<std::vector<main()::S> >; T = std::vector<main()::S>]':
FOO/include/range-v3/range/v3/iterator/concepts.hpp:561:19: required by substitution of 'template<class I> using apply = ranges::detail::enable_if_t<(bool)(readable<I>), I> [with I = std::vector<main()::S>]'
FOO/include/range-v3/range/v3/algorithm/find.hpp:48:15: required by substitution of 'template<class I, class S, class V, class P> constexpr concepts::return_t<I, typename std::enable_if<(((input_iterator<I> && sentinel_for<S, I>) && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P>::apply<I>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(I, S, const V&, P) const [with I = std::vector<main()::S>; S = int; V = int (main()::S::*)() const; P = ranges::identity]'
test.cpp:14:31: required from here
FOO/include/range-v3/std/detail/associated_types.hpp:73:15: error: no class template named 'apply' in 'struct ranges::detail::enable_if<false>'
73 | using enable_if_t = typename enable_if<B>::template apply<T>;
| ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/range_fwd.hpp:24,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:18,
from test.cpp:1:
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: candidate: 'template<class Rng, class V, class P> constexpr concepts::return_t<typename ranges::detail::if_then<forwarding_range_<R> >::apply<decltype (ranges::_::begin(declval<Rng&>())), ranges::dangling>, typename std::enable_if<((input_range<Rng> && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P1>::apply<decltype (ranges::_::begin(declval<Rng&>()))>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(Rng&&, const V&, P) const'
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:60:24: note: in expansion of macro 'RANGES_FUNC'
60 | constexpr auto RANGES_FUNC(find)(Rng && rng, V const & val, P proj = P{})
| ^~~~~~~~~~~
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: template argument deduction/substitution failed:
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:60:24: note: in expansion of macro 'RANGES_FUNC'
60 | constexpr auto RANGES_FUNC(find)(Rng && rng, V const & val, P proj = P{})
| ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/iterator/access.hpp:22,
from FOO/include/range-v3/range/v3/iterator/concepts.hpp:30,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:22,
from test.cpp:1:
FOO/include/range-v3/std/detail/associated_types.hpp: In substitution of 'template<bool B, class T> using enable_if_t = typename ranges::detail::enable_if::apply<T> [with bool B = ranges::indirectly_regular_unary_invocable<int (main()::S::*)() const, __gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> > >; T = ranges::detail::projected_<__gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> >, int (main()::S::*)() const>]':
FOO/include/range-v3/range/v3/iterator/concepts.hpp:552:19: required by substitution of 'template<class Proj> template<class I> using apply = ranges::detail::enable_if_t<(bool)(indirectly_regular_unary_invocable<Proj, I>), ranges::detail::projected_<I, Proj> > [with I = __gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> >; Proj = int (main()::S::*)() const]'
FOO/include/range-v3/range/v3/algorithm/find.hpp:61:15: required by substitution of 'template<class Rng, class V, class P> constexpr concepts::return_t<typename ranges::detail::if_then<forwarding_range_<R> >::apply<decltype (ranges::_::begin(declval<Rng&>())), ranges::dangling>, typename std::enable_if<((input_range<Rng> && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P1>::apply<decltype (ranges::_::begin(declval<Rng&>()))>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(Rng&&, const V&, P) const [with Rng = const std::vector<main()::S>&; V = int; P = int (main()::S::*)() const]'
test.cpp:14:31: required from here
FOO/include/range-v3/std/detail/associated_types.hpp:73:15: error: no class template named 'apply' in 'struct ranges::detail::enable_if<false>'
73 | using enable_if_t = typename enable_if<B>::template apply<T>;
| ^~~~~~~~~~~
shell returned 1
Making the projection a lambda...
ranges::find( ss, 1, [](auto const& s){ return s.get_i(); } );
...works but seems wasted typing.
Referring directly to the data member...
ranges::find(ss, 1, &S::i);
...works but is not possible if it should be encapsulated behind a const getter, transformer, etc.
What am I doing wrong? Can I not use a pointer-to-member-function as projection? Is it intended?
Edit: clang++ (also on MSYS2) does work here. So I guess this must be a bug in g++. Off to Bugzilla I go... edit: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94973
This is caused by GCC on Windows defaulting to -fms-extensions for compatibility with Microsoft compilers, and that pulling in a non-standard extension that introduces ambiguity between pointers-to-member-data vs -functions written as instance.*member.
We can pass -fno-ms-extensions to stop this and compile such code succesfully, until GCC remove that particular extension, which seems unnecessary and unhelpful nowadays.
Jonathan answered this on my GCC bug 94973:
Jonathan Wakely 2020-05-06 16:26:58 UTC
Aha, the same problem happens on linux if I compile with -fms-extensions
This is the old MS extension that causes x.*f to be accepted when f is a pointer to member function, which should only be valid when used as (x.*f)().
That causes ranges::invoke to think that the projection is a pointer to data member, when actually it's a pointer to member function.
See also PR 94771 comment 4.
Jason, do we want to disable that extension in SFINAE contexts?
Jonathan Wakely 2020-05-06 16:47:42 UTC
They're on by default for mingw, for compatibility with the MS compiler (but in this case it seems the relevant extension is ancient history).

Generating virtual methods in template base class for optional override in inheriting class

What I want is a template class that, given tuple, and used as a base class, provides method with default behavior for each type of element in tuple. These methods should be virtual, so that they could be overridden in inheriting class.
Code below does exactly that:
#include <tuple>
#include <iostream>
struct A {};
struct B {};
struct C {};
template <typename Class, uint16_t tag>
struct def {
using message_type = Class;
static constexpr uint16_t class_tag = tag;
};
// (3) adding "constexpr" or "const" causes compilation failure
auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
template <typename T> // (1)
struct base_handler_t {
virtual void h(T const& t) { std::cout << "base_handler_t\n"; }
};
template <typename ...Ts> // (2) - adding "const" to "std::tuple<Ts...>" in line below makes code work again if "t" is constant
struct base_handler_t<std::tuple<Ts...>> : public base_handler_t<typename Ts::message_type>...{
using base_handler_t<typename Ts::message_type>::h...;
};
struct service_t : public base_handler_t<decltype(t)> {
using base_handler_t<decltype(t)>::h;
void h(B const & b) {
std::cout << "service_t\n";
}
};
int main() {
service_t n;
n.h(A());
n.h(B());
}
EDIT AFTER FINDING EXACT AND MINIMAL EXAMPLE THAT BREAKS CODE:
Code above works fine when entered as-is, but if line below comment (3) (about adding constexpr to definition of t) is changed to either:
const auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
or
constexpr auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
code fails to compile. Compiler claims that:
x.cc: In function ‘int main()’:
x.cc:35:16: error: no matching function for call to ‘service_t::h(A)’
35 | n.h(A());
| ^
x.cc:28:14: note: candidate: ‘void service_t::h(const B&)’
28 | void h(B const & b) {
| ^
x.cc:28:26: note: no known conversion for argument 1 from ‘A’ to ‘const B&’
28 | void h(B const & b) {
| ~~~~~~~~~~^
x.cc:18:22: note: candidate: ‘void base_handler_t<T>::h(const T&) [with T = const std::tuple<def<A, 0>, def<B, 1>, def<C, 2> >]’
18 | virtual void h(T const& t) { std::cout << "base_handler_t\n"; }
| ^
x.cc:18:33: note: no known conversion for argument 1 from ‘A’ to ‘const std::tuple<def<A, 0>, def<B, 1>, def<C, 2> >&’
18 | virtual void h(T const& t) { std::cout << "base_handler_t\n"; }
| ~~~~~~~~~^
I assumed that when it comes to templates, there is no real difference if type is extracted from constant or variable.
Code starts to work after changing in line below (2):
struct base_handler_t<std::tuple<Ts...>> : ...
to
struct base_handler_t<const std::tuple<Ts...>> : ...
Why is it so? Is it because std::tuple<Ts...> does not match const std::tuple<Ts...> exactly? What are exact rules that govern this case?
Thanks in advance.
ORIGINAL PLEAD FOR HELP:
In original code base methods for each type (A, B and C in example above) are defined in "service_t" class by hand. I attempted to solve this problem exactly as in example above. As long as all methods were present, code still worked fine. As soon as I commented out single method I received error that there is no matching method to call, followed by list of possible matches. There was match for method with argument std::tuple<def<.... and so on - it seems that while in my snippet above everything works fine (so one method is generated for each tuple element type), there's something preventing larger code base from matching template (2) and instead it uses template (1).
I'd like to hear any idea why this would fail. Thanks in advance.
With
auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
decltype(t) is std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>
If t is const qualified: const auto t = /*..*/, then, decltype(t) is const std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>.
So for base_handler_t<decltype(t)>, base_handler_t<const std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>> match only primary template definition, not your specialization.
You might use instead base_handler_t<std::remove_const_t<decltype(t)>> or base_handler_t<std::decay_t<decltype(t)>> (remove reference and then cv qualifiers)

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.