So here is the scenario I have been to trying to work upon
#include <iostream>
#include <vector>
#include <string>
#include <complex>
class Double
{
private:
double dd;
std::vector<double> gg;
std::string ss;
public:
Double(double d,const std::string& s = std::string())
{
dd = d;
ss = s;
gg = std::vector<double>();
}
Double(const std::vector<double>& d,const std::string& s = std::string())
{
dd = 0;
ss = s;
gg = d;
}
double getdd() const { return dd; }
};
template<typename T>
Double getDouble(T t,const std::string& s = std::string())
{
return Double(t,s);
}
std::string to_string(Double d)
{
double zd = d.getdd() + 1.0;
return std::to_string(zd);
}
class TEST
{
public:
template<typename T_0>
TEST(const T_0 & var_0)
{
auto x = to_string(getDouble(var_0));
}
};
int main()
{
TEST ds ({3.4,"ssd"});
std::cout << "Reached heare\n";
return 0;
}
Essentially if I call
Double dd ({3.4,"ssd"});
everything works fine but if I try the same thing with the class TEST, I get the following error.
main.cpp: In function 'int main()':
main.cpp:57:25: error: no matching function for call to 'TEST::TEST(<brace-enclosed initializer list>)'
TEST ds ({3.4,"ssd"});
^
main.cpp:48:5: note: candidate: template<class T_0> TEST::TEST(const T_0&)
TEST(const T_0 & var_0)
^
main.cpp:48:5: note: template argument deduction/substitution failed:
main.cpp:57:25: note: couldn't deduce template parameter 'T_0'
TEST ds ({3.4,"ssd"});
^
main.cpp:44:8: note: candidate: constexpr TEST::TEST(const TEST&)
class TEST
^
main.cpp:44:8: note: no known conversion for argument 1 from '<brace-enclosed initializer list>' to 'const TEST&'
main.cpp:44:8: note: candidate: constexpr TEST::TEST(TEST&&)
main.cpp:44:8: note: no known conversion for argument 1 from '<brace-enclosed initializer list>' to 'TEST&&'
I have tried to deduce the parameters adding a constructor as below
Double (std::initializer_list<boost::any> xx)
but it hasnt worked for me.
Is there any way to get past this issue ? I cant use the explicit type at the callsite since the elements of the initializer list are not of the same type.
Unfortunately the braced-init-list doesn't have any type; the template parameter T_0 can't be deduced from it.
You can use parameter pack instead.
template<typename... T_0>
TEST(const T_0 & ... var_0)
{
auto x = to_string(getDouble(var_0...));
}
then use it like
TEST ds (3.4,"ssd");
LIVE
This call works
Double dd ({3.4,"ssd"});
because there is appropriate constructor
Double(double d,const std::string& s = std::string())
{
dd = d;
ss = s;
gg = std::vector<double>();
}
This call
TEST ds ({3.4,"ssd"});
does not work because the template constructor has only one parameter
template<typename T_0>
TEST(const T_0 & var_0)
{
auto x = to_string(getDouble(var_0));
}
Pay attention to that this construction {3.4,"ssd"} does not have a type. SO the compiler reports that
main.cpp:48:5: note: template argument deduction/substitution failed:
main.cpp:57:25: note: couldn't deduce template parameter 'T_0'
TEST ds ({3.4,"ssd"});
^
Using boost:variant:
#include <tuple>
#include <iostream>
#include <boost/variant.hpp>
template <size_t n, typename... T>
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
if (i == n)
return std::get<n>(tpl);
else if (n == sizeof...(T) - 1)
throw std::out_of_range("Out of Index");
else
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
}
template <typename... T>
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
return _tuple_index<0>(i, tpl);
}
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
int main()
{
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
for(int i = 0; i != tuple_len(t); ++i) {
std::cout << tuple_index(i, t) << std::endl; // works with boost
}
}
Replace boost::variant by std::variant, added a helper to stream std::variant:
#include <tuple>
#include <iostream>
#include <variant>
template <size_t n, typename... T>
std::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
if (i == n)
return std::get<n>(tpl);
else if (n == sizeof...(T) - 1)
throw std::out_of_range("Out of Index");
else
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
}
template <typename... T>
std::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
return _tuple_index<0>(i, tpl);
}
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
// added helper to stream std::variant
template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
std::visit([&](auto && arg){ s << arg;}, v);
return s;
}
int main()
{
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
for(int i = 0; i != tuple_len(t); ++i) {
std::cout << tuple_index(i, t) << std::endl; // doesn't work anymore
}
}
The compilation remain error:
$ clang++ -v [17:37:47]
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
$ clang++ -std=c++17 isostd.cpp
isostd.cpp:8:16: error: no viable conversion from returned value of type 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type' (aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to function return
type 'std::variant<basic_string<char>, double, double, int>'
return std::get<n>(tpl);
^~~~~~~~~~~~~~~~
isostd.cpp:12:16: note: in instantiation of function template specialization '_tuple_index<1, std::__1::basic_string<char>, double, double, int>' requested here
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
^
isostd.cpp:16:12: note: in instantiation of function template specialization '_tuple_index<0, std::__1::basic_string<char>, double, double, int>' requested here
return _tuple_index<0>(i, tpl);
^
isostd.cpp:35:22: note: in instantiation of function template specialization 'tuple_index<std::__1::basic_string<char>, double, double, int>' requested here
std::cout << tuple_index(i, t) << std::endl; // doesn't work anymore
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1142:3: note: candidate constructor not viable: no known conversion from 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type'
(aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to 'const std::__1::variant<std::__1::basic_string<char>, double, double, int> &' for 1st argument
variant(const variant&) = default;
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1143:3: note: candidate constructor not viable: no known conversion from 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type'
(aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to 'std::__1::variant<std::__1::basic_string<char>, double, double, int> &&' for 1st argument
variant(variant&&) = default;
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1155:13: note: candidate template ignored: substitution failure [with _Arg = const double &, $1 = 0, $2 = 0, $3 = 0, _Tp = double]: no member named 'value' in
'std::__1::__find_detail::__find_unambiguous_index_sfinae<double, std::__1::basic_string<char>, double, double, int>'
constexpr variant(_Arg&& __arg) noexcept(
^
1 error generated.
How can I properly replace boost:variant by std::variant?
I aware of the reference: What are the differences between std::variant and boost::variant?
Boost.Variant includes recursive_variant, which allows a variant to contain itself. They're essentially special wrappers around a pointer to a boost::variant, but they are tied into the visitation machinery.
If I understand correctly, there is no way to finish the replace?
As you have duplicate types in your variant some of the constructors are disabled:
This overload only participates in overload resolution if there is exactly one occurrence of T in Types...
You need to use the constructor with an explicit type index:
return std::variant<T...>(std::in_place_index<n>, std::get<n>(tpl));
Your original boost code has undefined behaviour:
Each type specified as a template argument to variant must be distinct after removal of qualifiers. Thus, for instance, both variant<int, int> and variant<int, const int> have undefined behavior.
The standard libary implementation does support duplicate types and therefore prevents you from accidentally constructing an ambigous variant. For example what should the following do:
variant<std::string, double, double, int> t = 4.5;
With boost it is UB, either of the double values might be initialised or it might do something completely different. The standard library explitly makes this a compiler error so that you have to choose which of your doubles you want to initialise.
While writing some template code, I ran into <unresolved overloaded function type> errors which can be reduced to the following.
template <int N>
auto bar()
{
return N;
}
int main(int, char* [])
{
auto foo = [] (auto func) {
return func();
};
foo(bar<3>);
}
With the errors being:
unresolved_overload.cpp: In function 'int main(int, char**)':
unresolved_overload.cpp:26:28: error: no match for call to '(main(int, char**)::<lambda(auto:1)>) (<unresolved overloaded function type>)'
std::cout << foo(bar<3>) << std::endl;
^
unresolved_overload.cpp:21:29: note: candidate: template<class auto:1> constexpr main(int, char**)::<lambda(auto:1)>::operator decltype (((const main(int, char**)::<lambda(auto:1)>*)((const main(int, char**)::<lambda(auto:1)>* const)0))->operator()(static_cast<auto:1&&>(<anonymous>))) (*)(auto:1)() const
auto foo = [] (auto func) {
^
unresolved_overload.cpp:21:29: note: template argument deduction/substitution failed:
unresolved_overload.cpp:26:28: note: couldn't deduce template parameter 'auto:1'
std::cout << foo(bar<3>) << std::endl;
^
unresolved_overload.cpp:21:29: note: candidate: template<class auto:1> main(int, char**)::<lambda(auto:1)>
auto foo = [] (auto func) {
^
unresolved_overload.cpp:21:29: note: template argument deduction/substitution failed:
unresolved_overload.cpp:26:28: note: couldn't deduce template parameter 'auto:1'
std::cout << foo(bar<3>) << std::endl;
If we replace the auto-return with the explicit return type, int, the example will compile fine.
Why does auto-return run into these issues? I looked into template argument deduction and substitution but the search was largely unfruitful. I thought it might have something to do with the order of template instantiation / etc but couldn't make too much sense of it...
Per AndyG's suggestion, I found the same issue on GCC's bug list. Bug 64194. First reported in 2014. Thus the conclusion seems to be that this is a GCC bug and thankfully not another special case for templates.
Working around this just requires having something else to trigger the instantiation (e.g. assign to a variable, a using declaration).
Try this:
template <typename func>
auto bar(func&& f)->decltype(f())
{
return f();
}
int main()
{
int i = 100;
auto f = [=]()
{
return i;
};
bar(f);
return 0;
}
I'm trying to use a template class (here Foo), with a basic type like:
hana::tuple<hana::pair<hana::type<int>, Runtime>> with Runtime a class which obiviously can't be constepxr.
But the type can be construct in several way that's why I use:
hana::tuple<hana::pair<hana::type<int>, hana::type<Runtime>>> to do the work at compile time.
So the question is basically how convert from the first tuple type to the second one. I wonder if there is something in hana that could help me. Or even better, some tips on that kind of "conversion".
namespace hana = boost::hana;
using namespace hana::literals;
struct Runtime { std::vector<int> data; };
template < typename T >
struct Foo {
T data;
};
constexpr decltype(auto) convertMap(auto storageMap) {
return hana::make_type(hana::transform(
storageMap,
[] (auto pair) {
return hana::make_pair(
hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type {});
}));
}
int main() {
constexpr auto map = hana::make_tuple(
hana::make_pair(hana::type_c<int>, hana::type_c<Runtime>)
);
constexpr auto result = convertMap(map);
static_assert(result ==
hana::type_c<hana::tuple<hana::pair<hana::type<int>, Runtime>>>);
Foo<typename decltype(result)::type> test;
}
As you can see I tried some c++1z convertMap with hana::transform and lambdas, but the second tuple can't be constexpr, so I can't pass it to hana::make_type hoping to get a hana::type_c.
test.cpp: In function ‘int main()’:
test.cpp:70:41: error: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ called in a constant expression
constexpr auto result = convertMap(map);
^
test.cpp:53:27: note: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ is not usable as a constexpr function because:
constexpr decltype(auto) convertMap(auto storageMap) {
^~~~~~~~~~
test.cpp:53:27: error: temporary of non-literal type ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ in a constant expression
In file included from /usr/include/boost/hana/detail/struct_macros.hpp:29:0,
from /usr/include/boost/hana/adapt_adt.hpp:15,
from lib/hana/include/boost/hana.hpp:59,
from test.cpp:1:
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ is not literal because:
struct tuple
^~~~~
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ has a non-trivial destructor
All you care about is a type - you can therefore hide the computation of the type in a non-constexpr implementation function, and afterwards call it with decltype(...){} to "force constexpr":
template <typename T>
decltype(auto) convertMapImpl(T storageMap)
{
return hana::make_type(hana::transform(storageMap, [](auto pair) {
return hana::make_pair(hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type{});
}));
}
template <typename T>
constexpr decltype(auto) convertMap(T storageMap)
{
return decltype(convertMapImpl(storageMap)){};
}
live wandbox example
Also note that using auto in a function signature is a gcc extension - you should use a template parameter instead to be standard-compliant.
Consider the following code:
#include <unordered_map>
#include <tuple>
namespace Test
{
template<typename State>
struct StateTableEntry
{
State state;
};
template<typename State>
using StateRow = std::unordered_map<int,StateTableEntry<State>>;
template<typename StateRowValueType>
auto& entryBAD(StateRowValueType& row)
{ return row.second; }
template<typename StateRowValueType>
auto entryOK(StateRowValueType& row) -> decltype((row.second))
{ return row.second; }
}
template<class T,int I,class O> std::enable_if_t<I==std::tuple_size<T>::value>
for_each_index_of(O&){}
template<class Tuple, int startingIndex=0, class Operation>
std::enable_if_t<startingIndex<std::tuple_size<Tuple>::value>
for_each_index_of(const Operation& operation)
{
operation(std::integral_constant<std::size_t,startingIndex>());
for_each_index_of<Tuple,startingIndex+1>(operation);
}
int main()
{
for_each_index_of<std::tuple<int>>([](const auto&)
{
Test::StateRow<long> stateRow;
for(auto& rowElement : stateRow)
{
auto& state(entryBAD(rowElement).state);
state=1;
}
});
}
If I try to compile it as is, gcc tells me
test.cpp: In instantiation of ‘main()::<lambda(const auto:1&)> [with auto:1 = std::integral_constant<long unsigned int, 0ul>]’:
test.cpp:29:14: required from ‘std::enable_if_t<(startingIndex < std::tuple_size<_Tp>::value)> for_each_index_of(const Operation&) [with Tuple = std::tuple<int>; int startingIndex = 0; Operation = main()::<lambda(const auto:1&)>; std::enable_if_t<(startingIndex < std::tuple_size<_Tp>::value)> = void]’
test.cpp:43:6: required from here
test.cpp:40:44: error: use of ‘template<class StateRowValueType> auto& Test::entryBAD(StateRowValueType&)’ before deduction of ‘auto’
auto& state(entryBAD(rowElement).state);
^
test.cpp:40:44: error: use of ‘auto& Test::entryBAD(StateRowValueType&) [with StateRowValueType = std::pair<const int, Test::StateTableEntry<long int> >]’ before deduction of ‘auto’
test.cpp:24:1: error: ‘std::enable_if_t<(I == std::tuple_size<_Tp>::value)> for_each_index_of(O&) [with T = std::tuple<int>; int I = 1; O = const main()::<lambda(const auto:1&)>; std::enable_if_t<(I == std::tuple_size<_Tp>::value)> = void]’, declared using local type ‘const main()::<lambda(const auto:1&)>’, is used but never defined [-fpermissive]
for_each_index_of(O&){}
^
But if I move the conde of lambda out of the lambda or replace call of entryBAD with entryOK, for some reason compilation succeeds. Same success if I move definition of entryBAD out of namespace Test.
Also, clang++ 3.6 compiles in all cases without complaints.
Is gcc right or is it a bug in it? If gcc is right, then what's wrong with the code?