Question

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.

Was it helpful?

Solution

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:

  1. know the type statically:

    const auto& baz = boost::get<double>(variant); 
    
  2. return the typeid instead:

    std::type_info const& typeinfo_value = variant.type();
    
  3. 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";
    }
    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top