1

I have an std::array of boost::variant object, and I'm trying to create a boost::static_visitor which visits an element in the array, returning a reference to something in each of the variant member types. That's a mouthful, so here's a code snippet mimicking my implementation:

#include <boost/variant.hpp>
#include <array>

struct SomeType {};

struct A {
  SomeType something;
  SomeType& someMethod() { return something; }
};

struct B {
  SomeType something;
  SomeType& someMethod() { return something; }
};

struct C {
  SomeType something;
  SomeType& someMethod() { return something; }
};

typedef boost::variant<A, B, C> MyVariant;

class SomeVisitor : public boost::static_visitor<> {
public:
  template<typename T>
  SomeType& operator()(T& operand) const {
    return operand.someMethod();
  }
};

class MyVariants {
public:
  SomeType* getSomething(unsigned index);

private:
  static const size_t count = 100;
  std::array<MyVariant, count> variants_;
};

SomeType* MyVariants::getSomething(unsigned index) {
  if(index < count) {
    MyVariant& variant = variants_[index];
    SomeType& something = boost::apply_visitor(SomeVisitor(), variant);
    return &something;
  }
  else {
    return nullptr;
  }
}

This snippet compiles with clang 3.6.2, but gcc 5.3.1 spits out the following (followed by a few dozen errors from the boost variant headers)

test.cpp:43:47: error: invalid initialization of non-const reference of type 'SomeType&' from an rvalue of type 'boost::static_visitor<>::result_type {aka void}'
     SomeType& something = boost::apply_visitor(SomeVisitor(), variant);

All the errors seem to say the same thing - the visitor's return type is void, and I can't bind that to a SomeType&. I don't think there are any syntax errors with my implementation of SomeVisitor since this compiles fine with clang.

This question and this question show similar errors generated by a boost::static_visitor, and both were explained by C++'s most-vexing-parse. In both those questions, the issue was something like this (using types from my snippet above):

MyVariant variant(A());
SomeType& something = boost::apply_visitor(SomeVisitor(), variant);

In this context, I can understand how the most vexing parse applies. MyVariant variant(A()); might be ambiguous to the compiler. I don't know how this applies to my snippet though, since MyVariant& variant = variants_[index] seems pretty explicit. I don't know if these questions are even related to my issues.

Any advice/help would be appreciated

Community
  • 1
  • 1
Matt K
  • 598
  • 5
  • 19
  • 2
    You have to specify the return type in static_visitor's template argument list. Leaving it empty is telling the compiler that the functor will return void. – Richard Hodges Mar 31 '16 at 23:18
  • Thanks! That was exactly the problem. I made a `boost::static_visitor` with a return value a few weeks ago (with the right syntax) so I'm doubly embarrassed – Matt K Mar 31 '16 at 23:24
  • We've all done it. Note that in later versions of boost with c++14 you don't need a static visitor. A lambda with `auto&` argument type will suffice. – Richard Hodges Mar 31 '16 at 23:32

1 Answers1

1

Comment provided as an answer:

You have to specify the return type in static_visitor's template argument list. Leaving it empty is telling the compiler that the functor will return void.

class SomeVisitor : public boost::static_visitor<SomeType&> {
public:
  template<typename T>
  SomeType& operator()(T& operand) const {
    return operand.someMethod();
  }
};

Alternatively, in later versions of boost with c++14:

auto& something = boost::apply_visitor([](auto& x) { return x.someMethod(); }, 
                                       variant);
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142