Writing Lambda signatures for lazy initialization - c++

#include <iostream>
#include <string>
typedef std::string S;
template <typename T>
static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
bool assigned = false;
if (!assigned) {
// invoke creationSpren with passed arguments
// assign
}
}
int main()
{
auto& xx = []() {
return new std::string("abc");
};
auto& zzz = getOrCreate<S>(xx);
}
note: this code does not compile, that is the problem I am trying to solve.
however, I wrote this minimum example to illustrate the problem, it is as barebones as possible.
What I'm trying to achieve is simple, to use lambdas to achieve lazy initialization of an object, when it is needed (i.e. when a retrieve fails, it calls the lambda and assigns the object (i.e. stores it) and returns it)
What I have problems with, as I have no experience with lambdas is both the signatures.
That is what I am asking, how to write the 2 lambda signatures. thanks.
and yes, it needs to be templated.
verbatim errors
<source>: In lambda function:
<source>:7:45: error: expected '{' before ')' token
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ^
<source>: At global scope:
<source>:7:46: error: expected ')' before 'creationSpren'
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ~ ^~~~~~~~~~~~~~
| )
<source>:7:63: error: expected ';' before '{' token
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ^~
| ;
<source>: In function 'int main()':
<source>:18:16: error: cannot bind non-const lvalue reference of type 'main()::<lambda()>&' to an rvalue of type 'main()::<lambda()>'
18 | auto& xx = []() {
| ^~~~~~
19 | return new std::string("abc");
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 | };
| ~
<source>: In instantiation of 'std::__cxx11::basic_string<char>* getOrCreate<std::__cxx11::basic_string<char> >':
<source>:22:17: required from here
<source>:7:33: error: cannot convert '<lambda()>' to 'std::__cxx11::basic_string<char>*' in initialization
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ^~~~~~~~~~~~
| |
| <lambda()>
<source>:22:31: error: 'getOrCreate<std::__cxx11::basic_string<char> >' cannot be used as a function
22 | auto& zzz = getOrCreate<S>(xx);
| ~~~~~~~~~~~~~~^~~~
ASM generation compiler returned: 1
<source>: In lambda function:
<source>:7:45: error: expected '{' before ')' token
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ^
<source>: At global scope:
<source>:7:46: error: expected ')' before 'creationSpren'
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ~ ^~~~~~~~~~~~~~
| )
<source>:7:63: error: expected ';' before '{' token
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ^~
| ;
<source>: In function 'int main()':
<source>:18:16: error: cannot bind non-const lvalue reference of type 'main()::<lambda()>&' to an rvalue of type 'main()::<lambda()>'
18 | auto& xx = []() {
| ^~~~~~
19 | return new std::string("abc");
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 | };
| ~
<source>: In instantiation of 'std::__cxx11::basic_string<char>* getOrCreate<std::__cxx11::basic_string<char> >':
<source>:22:17: required from here
<source>:7:33: error: cannot convert '<lambda()>' to 'std::__cxx11::basic_string<char>*' in initialization
7 | static inline T* getOrCreate( ( []() -> auto) creationSpren *) {
| ^~~~~~~~~~~~
| |
| <lambda()>
<source>:22:31: error: 'getOrCreate<std::__cxx11::basic_string<char> >' cannot be used as a function
22 | auto& zzz = getOrCreate<S>(xx);
| ~~~~~~~~~~~~~~^~~~
Execution build compiler returned: 1

If you look at the standard library and their functions which takes a callable object, it uses templates.
I recommend that for your function as well:
template<typename T, typename F>
static inline T* getOrCreate(F creationSpren)
{
// ...
}
There's another problem with your current function: The variable assigned is a normal local variable. It will be created and initialized to false each time getOrCreate is called.
You need to make it a static local variable.
If you need to pass argument to getOrCreate that are then forwarded to the creationSpren function, then use template parameter packs:
#include <utility>
#include <iostream>
template<typename T, typename F, typename ...A>
static inline T* getOrCreate(F creationSpren, A&& ...args)
{
creationSpren(std::forward<A>(args)...);
return nullptr;
}
int main()
{
auto lambda_noargs = []() { std::cout << "No arguments\n"; };
auto lambda_twoargs = [](int, int) { std::cout << "Two arguments\n"; };
getOrCreate<int>(lambda_noargs);
getOrCreate<int>(lambda_twoargs, 1, 2);
}

what you have now is just a complicated version of static local variable. (usually used in singleton)
int main(){
auto xx = []{
static std::string* v = new std::string("abc");
return v;
};
std::string* a = xx();
std::string* b = xx();
assert(a==b);
delete a; // manually cleanup as you `new` it
}
https://godbolt.org/z/GnsGaqnx3

Related

Function with template specialization in class without wrapping by class

I have a class and I wanna specialize a function in that class.
I need something like this:
class A
{
public:
template <typename T>
T fun()
{
throw new Exception("Unsupported template param");
}
template <>
bool fun<bool>()
{
return true;
}
template <>
float fun<float>()
{
return 5.6f;
}
template <>
double fun<double>()
{
return 5.684;
}
};
It works if I use the function without a class but if I put the function to the class I will get a lot of errors.
Example:
template <>
bool fun<bool>()
{
return true;
}
template <>
float fun<float>()
{
return 5.6f;
}
template <>
double fun<double>()
{
return 5.684;
}
It works. But when I declare fun in some class it doesn't work.
Errors: (Sorry for Russian language but my OS is translated to russian)
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:12:15: ошибка: явная специализация в не-namespace области «class A»
12 | template <>
| ^
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:13:10: ошибка: template-id «fun<bool>» in declaration of primary template
13 | bool fun<bool>()
| ^~~~~~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:18:15: ошибка: явная специализация в не-namespace области «class A»
18 | template <>
| ^
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:19:11: ошибка: template-id «fun<float>» in declaration of primary template
19 | float fun<float>()
| ^~~~~~~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:19:11: ошибка: «float A::fun()» cannot be overloaded with «bool A::fun()»
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:13:10: замечание: предыдущая декларация «bool A::fun()»
13 | bool fun<bool>()
| ^~~~~~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:24:15: ошибка: явная специализация в не-namespace области «class A»
24 | template <>
| ^
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:25:12: ошибка: template-id «fun<double>» in declaration of primary template
25 | double fun<double>()
| ^~~~~~~~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:25:12: ошибка: «double A::fun()» cannot be overloaded with «bool A::fun()»
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:13:10: замечание: предыдущая декларация «bool A::fun()»
13 | bool fun<bool>()
| ^~~~~~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp: В функции-члене «T A::fun()»:
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:9:19: ошибка: expected type-specifier before «Exception»
9 | throw new Exception("unsupported colormodel");
| ^~~~~~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp: В функции «int main(int, const char**)»:
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:33:18: ошибка: нет декларации «fun» в этой области видимости
33 | auto test1 = fun<float>();
| ^~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:33:22: ошибка: expected primary-expression before «float»
33 | auto test1 = fun<float>();
| ^~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:34:22: ошибка: expected primary-expression before «double»
34 | auto test2 = fun<double>();
| ^~~~~~
/home/aleksey/Документы/Проекты/tests/color-c/source/app.cpp:35:22: ошибка: expected primary-expression before «int»
35 | auto test3 = fun<int>();
I googled some similar questions about this purpose but most of the answers suggest only solutions with templates class, not template function in non-templated class. However, I've found this question. But the date of this question is 2014. Since the date of this one c++ get many changes. Maybe is there any way to specialize a function in a class without wrapping the function to separate class?

Why does not work with lambda with template

auto bind2nd = [] < auto func_object,auto second_arg>(){
return [=](auto&& first_arg){
return func_object(first_arg,second_arg);
};
};
auto h =bind2nd.template operator()<std::greater<int>(),5>();
compiler result:
<source>:9:60: error: no matching function for call to '<lambda()>::operator()<std::greater<int>(), 5>()'
9 | auto x =bind2nd.template operator()<std::greater<int>(),5>();
| ^
<source>:3:16: note: candidate: 'template<auto func_object, auto second_arg> <lambda()>'
3 | auto bind2nd = [] < auto func_object,auto second_arg>(){
| ^
<source>:3:16: note: template argument deduction/substitution failed:
<source>:9:60: error: type/value mismatch at argument 1 in template parameter list for 'template<auto func_object, auto second_arg> <lambda()>'
9 | auto x =bind2nd.template operator()<std::greater<int>(),5>();
| ^
<source>:9:60: note: expected a constant of type 'auto', got 'std::greater<int>()'
<source>:9:60: note: ambiguous template argument for non-type template parameter is treated as function type
I want to use lambda with template and it does not work.
But I can run that :
auto x =[]<auto t>(){
return t;
};
auto test = []<auto func_object,auto second_arg>(){
return [=](auto&& first_arg){
return func_object.template operator()<second_arg>();
};
};
auto z =test.template operator()<x,5>();
int main(){
std::cout<<z(5);
}
It will print 5.
What's the right way to use lambda with template and how to fix this problem?
As a template argument std::greater<int>() is parsed as a function type (a function that takes no arguments and returns the class type std::greater<int>). This is where you can use curly brackets to help the compiler differentiate:
auto h = bind2nd.template operator()<std::greater<int>{}, 5>();

Inlining of a recursive function

When I try to compile this code:
#include <iostream>
#include <limits.h>
// End recursive template-expansion of function select below.
template <typename Type>
static inline constexpr Type select(unsigned index)
{ return Type(); }
// Select one of the items passed to it.
// e.g. select(0, a, b, c) = a; select(1, a, b, c) = b; etc.
template <typename Type, typename... Params>
[[gnu::always_inline]]
static inline constexpr Type select(unsigned index, Type value, Params... values)
{ return index == 0 ? value : select<Type>(index - 1, values...); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_1(Type mask, Type shift, Type value)
{ return ((value & mask) >> shift) | ((value << shift) & mask); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
{
return i == 0
? value
: reflect_mask_helper_0(
i - 1,
reflect_mask_helper_1<Type>(
select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
1 << (i - 1),
value));
}
template <typename Type>
[[gnu::flatten]]
static inline constexpr Type reflect_mask(Type value)
{ return reflect_mask_helper_0(__builtin_ctz(sizeof(Type) * CHAR_BIT), value); }
int main(void) {
for (int i = 0; i < 65536; i++) {
std::cout << reflect_mask<uint16_t>(i) << std::endl;
}
}
gcc gives me an error saying the function reflect_mask_helper_0 cannot be inlined because it is recursive. However, the function select is also recursive, but gcc inlines it without complaining. What am I missing here?
(I need it to be recursive, since constexpr functions cannot contain loops under C++11.)
Error message:
% g++ test.cpp -O3 -march=native -c
test.cpp: In function ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’:
test.cpp:23:30: error: inlining failed in call to always_inline ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’: recursive inlining
23 | static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
| ^~~~~~~~~~~~~~~~~~~~~
test.cpp:27:28: note: called from here
27 | : reflect_mask_helper_0(
| ~~~~~~~~~~~~~~~~~~~~~^
28 | i - 1,
| ~~~~~~
29 | reflect_mask_helper_1<Type>(
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 | select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 | 0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 | 1 << (i - 1),
| ~~~~~~~~~~~~~
33 | value));
| ~~~~~~~
test.cpp: In function ‘int main()’:
test.cpp:23:30: error: inlining failed in call to always_inline ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’: recursive inlining
23 | static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
| ^~~~~~~~~~~~~~~~~~~~~
test.cpp:27:28: note: called from here
27 | : reflect_mask_helper_0(
| ~~~~~~~~~~~~~~~~~~~~~^
28 | i - 1,
| ~~~~~~
29 | reflect_mask_helper_1<Type>(
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 | select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 | 0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 | 1 << (i - 1),
| ~~~~~~~~~~~~~
33 | value));
| ~~~~~~~
select doesn't actually calls itself. It pops the front of the type list it received and then calls another specialization of select<Type, ...>. The trailing parameter pack is different. Since that "recursion" is essentially a finite set of nested function calls (different functions), GCC can see right through it, regardless of the run-time parameter.
But reflect_mask_helper_0 does call itself, with the same template arguments, indefinitely. GCC has no way to tell how deep this run-time recursion will go at run-time. Recall that a constexpr function is still a regular function that must be invocable at run-time.
If you check out the resulting assembly code, if you remove the always_inline and flatten attributes, you can see that gcc actually inlines everything correctly.
So, this issue is a QoI thing. Maybe, at that point, when always_inline handled, it cannot be inlined (hence the error message), but gcc decides to inline it afterwards anyways.
Btw., you can finetune gcc, and with a little modification to your code, gcc can compile it:
pass --param max-early-inliner-iterations=3 to gcc
remove the flatten attribute (no idea, why it matters...)
(So, actually, this issue has nothing to do with recursive calls - from the compiler standpoint, it doesn't matter whether the function is recursive, or not, it just follows the flow of the code - to a certain extent, of course. Here, recursive depth is just 4, it is not too hard to follow for a compiler)
Here’s the solution I’ve found, thanks to grek40’s comment and to StoryTeller’s answer.
(As for my previous problem with the unused function template instance left in the compiled binary, I solved it by compiling the original code — without the gnu::always_inline and gnu::flatten attributes — with the arguments -ffunction-sections -fdata-sections -Wl,--gc-sections.)
Now reflect_mask_helper_0 is inside a struct (because C++ doesn’t allow partial specialization of function templates), and the i parameter of the function became the Index parameter of the struct template.
#include <iostream>
#include <limits.h>
// End recursive template-expansion of function select below.
template <typename Type>
static inline constexpr Type select(unsigned index)
{ return Type(); }
// Select one of the items passed to it.
// e.g. select(0, a, b, c) = a; select(1, a, b, c) = b; etc.
template <typename Type, typename... Params>
[[gnu::always_inline]]
static inline constexpr Type select(unsigned index, Type value, Params... values)
{ return index == 0 ? value : select<Type>(index - 1, values...); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_1(Type mask, Type shift, Type value)
{ return ((value & mask) >> shift) | ((value << shift) & mask); }
template <typename Type, unsigned Index>
struct reflect_mask_helper_0
{
[[gnu::always_inline]]
static inline constexpr Type invoke(Type value)
{
return reflect_mask_helper_0<Type, Index - 1>::call(
reflect_mask_helper_1<Type>(
static_cast<Type>(select(Index - 1,
0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000)),
1 << (Index - 1),
value));
}
};
template <typename Type>
struct reflect_mask_helper_0<Type, 0>
{
[[gnu::always_inline]]
static inline constexpr Type invoke(Type value) { return value; }
};
template <typename Type>
static inline constexpr Type reflect_mask(Type value)
{ return reflect_mask_helper_0<Type, __builtin_ctz(sizeof(Type) * CHAR_BIT)>::invoke(value); }
int main(void) {
for (int i = 0; i < 65536; i++) {
std::cout << reflect_mask<uint16_t>(i) << std::endl;
}
}

Trying to pass a constexpr lambda and use it to explicitly specify returning type

I would like to use a function and pass a constexpr lambda. However, it only compiles successfully if I let the type be deduced through auto. Explicitly giving the type through -> std::array<event, l()> seems to fail (the first instance). Why is this?
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) -> std::array<event, l()> {
return {};
} // error
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) {
return std::array<event, (l())>{};
} // OK
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) -> decltype(l()) { return {}; }
// OK
Note that, the lambda returns a size_t.
gcc errors on this without a call (clang accepts it):
prog.cc:9:63: error: template argument 2 is invalid
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:42: error: invalid template-id
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^~~
prog.cc:9:61: error: use of parameter outside function body before '(' token
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^
prog.cc:9:23: error: deduced class type 'array' in function return type
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^~~
In file included from prog.cc:4:
/opt/wandbox/gcc-head/include/c++/9.0.1/array:94:12: note:
'template<class _Tp, long unsigned int _Nm> struct std::array' declared here
94 | struct array
| ^~~~~
prog.cc: In function 'int main()':
prog.cc:14:5: error: 'foo' was not declared in this scope
14 | foo([]() {return 3; });
| ^~~
Parameters to constexpr functions are not themselves constexpr objects - so you cannot use them in constant expressions. Both of your examples returning arrays are ill-formed because there is no valid call to them.
To understand why, consider this nonsense example:
struct Z { int i; constexpr int operator()() const { return i; }; };
template <int V> struct X { };
template <typename F> constexpr auto foo(F f) -> X<f()> { return {}; }
constexpr auto a = foo(Z{2});
constexpr auto b = foo(Z{3});
Z has a constexpr call operator, and this is well-formed:
constexpr auto c = Z{3}();
static_assert(c == 3);
But if the earlier usage were allowed, we'd have two calls to foo<Z> that would have to return different types. This could only fly if the actual value f were the template parameter.
Note that clang compiling the declaration is not, in of itself, a compiler error. This is a class of situations that are ill-formed, no diagnostic required.

decltype and boost::variant - retrieve current value

I have the following code:
#include <boost/variant.hpp>
#include <iostream>
#include <string>
boost::variant<int, double, std::string> variant;
template <typename FirstArg, typename... OtherArgs>
auto bar(const std::type_info& variant_type_info) -> decltype(typeid(FirstArg) == variant_type_info ? boost::get<FirstArg>(variant) : bar<OtherArgs...>(variant_type_info))
{
if (typeid(FirstArg) == variant_type_info)
{
return boost::get<FirstArg>(variant);
}
return bar<OtherArgs...>(variant_type_info);
}
template <typename... VariantArgs>
auto foo(const boost::variant<VariantArgs...>& variant) -> decltype(bar<VariantArgs...>(variant.type()))
{
return bar<VariantArgs...>(variant.type());
}
int main()
{
variant = 0.5;
const auto& baz = foo(variant);
std::cout << baz << '\n';
}
It gives me the following errors:
main.cpp:9:135: error: 'bar' was not declared in this scope
auto bar(const std::type_info& variant_type_info) -> decltype(typeid(FirstArg) == variant_type_info ? boost::get<FirstArg>(variant) : bar<OtherArgs...>(variant_type_info))
^
main.cpp:9:148: error: expected primary-expression before '...' token
auto bar(const std::type_info& variant_type_info) -> decltype(typeid(FirstArg) == variant_type_info ? boost::get<FirstArg>(variant) : bar<OtherArgs...>(variant_type_info))
^
main.cpp:9:148: error: expected ')' before '...' token
main.cpp:9:135: error: 'bar' was not declared in this scope
auto bar(const std::type_info& variant_type_info) -> decltype(typeid(FirstArg) == variant_type_info ? boost::get<FirstArg>(variant) : bar<OtherArgs...>(variant_type_info))
^
main.cpp:9:54: error: expected type-specifier before 'decltype'
auto bar(const std::type_info& variant_type_info) -> decltype(typeid(FirstArg) == variant_type_info ? boost::get<FirstArg>(variant) : bar<OtherArgs...>(variant_type_info))
^
main.cpp:9:54: error: expected initializer before 'decltype'
main.cpp:20:69: error: 'bar' was not declared in this scope
auto foo(const boost::variant<VariantArgs...>& variant) -> decltype(bar<VariantArgs...>(variant.type()))
^
main.cpp:20:69: error: 'bar' was not declared in this scope
main.cpp:20:84: error: expected primary-expression before '...' token
auto foo(const boost::variant<VariantArgs...>& variant) -> decltype(bar<VariantArgs...>(variant.type()))
^
main.cpp:20:84: error: expected ')' before '...' token
main.cpp:20:69: error: 'bar' was not declared in this scope
auto foo(const boost::variant<VariantArgs...>& variant) -> decltype(bar<VariantArgs...>(variant.type()))
^
main.cpp:20:69: error: 'bar' was not declared in this scope
main.cpp:20:60: error: expected type-specifier before 'decltype'
auto foo(const boost::variant<VariantArgs...>& variant) -> decltype(bar<VariantArgs...>(variant.type()))
^
main.cpp:20:60: error: expected initializer before 'decltype'
main.cpp: In function 'int main()':
main.cpp:28:34: error: 'foo' was not declared in this scope
const auto& baz = foo(variant);
Coliru example - http://coliru.stacked-crooked.com/a/4467c33489b08359
I see that I can't refer to the same function in the decltype. Is there any workaround for such case? I am trying to write function to retrieve current value from boost::variant object.
Yes, there is a workaround in the general case. It's called:
Partial ordering. To get forward references as well, move methods into class namespace.
However
Nothing is gonna save you here. You cannot get hope to get the compiler to recover the static type of an argument, unless you already know it at compile time. This should be kinda obvious.
I mean, what should be the return type of foo()? How could it possibly contain all possible element types? Right. That's why you had the variant to begin with.
Three ways forward:
know the type statically:
const auto& baz = boost::get<double>(variant);
return the typeid instead:
std::type_info const& typeinfo_value = variant.type();
branch out and create separate paths to handle all cases. Boost has the static_visitor for this case:
struct Visitor : boost::static_visitor<std::string> {
std::string operator()(std::string const&) const { return "std::string"; }
std::string operator()(int) const { return "int"; }
std::string operator()(double) const { return "double"; }
template <typename... T>
std::string operator()(boost::variant<T...> const& v) const {
return boost::apply_visitor(*this, v);
}
};
int main()
{
static const Visitor _visitor;
variant = 0.5f;
std::cout << "The actual type is " << _visitor(variant) << "\n";
}