2

For a type Foo, which I want to use in a boost::variant, I wanted to set the default constructor to private, since it only should be allowed to be called by boost::variant.

Sadly I could not yet figure out the declaration magic of boost::variant and simply declaring

struct Foo {
private:
  Foo();
  template <class T1, class T2>
  friend class boost::variant<T1, T2>;
};

did not compile either. Is there any way to do this or do I need to keep Foo() public?

abergmeier
  • 13,224
  • 13
  • 64
  • 120

2 Answers2

3

[This answer addresses C++98/03 only; for modern code, see below]

The number of template parameters of boost::variant is given by BOOST_VARIANT_LIMIT_TYPES. You could use it by harnessing Boost.Preprocessor:

#include "boost/preprocessor/repetition/enum_params.hpp"

struct Foo {
private:
  Foo();

  template <BOOST_PP_ENUM_PARAMS(BOOST_VARIANT_LIMIT_TYPES, class T)>
  friend class boost::variant;
};

boost::variant is declared as a class template with BOOST_VARIANT_LIMIT_TYPES template parameters, so you must refer to it as such. This is the job for BOOST_PP_ENUM_PARAMS(a, b), which expands into a list of a comma-delimited items, each of which is b with a unique number appended. For example,

BOOST_PP_ENUM_PARAMS(5, class T)

will expand to:

class T0, class T1, class T2, class T3, class T4

Note that the above applies within the scope of the question, that is, limited to C++98/03. Since C++11, variadic templates exist and when these are available to Boost, BOOST_VARIANT_LIMIT_TYPES is not defined and the above code does not work. Boost offers an alternative which works both with and without variadics; see this answer for details.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    I think this there's no guarantee that the instantiating code is `boost::variant` itself, rather than one of its helper classes. But I haven't looked at implementation. Maybe it's part of the documented interface. – Cheers and hth. - Alf Apr 02 '15 at 09:51
  • @Cheersandhth.-Alf Good point. I answered the question of "how to befriend `boost::variant`," not the broader "how to use a private ctor with `boost::variant`." I will leave it to the OP to figure out whether it works for them or not; I'm willing to be outcompeted by a better answer, and/or withdraw mine in case it's found to be insufficient. – Angew is no longer proud of SO Apr 02 '15 at 09:54
  • I checked that `boost::variant` does the instantiation, before :). That said, the proposal does at least churn out a syntax error on VS2010 in identifier `BOOST_PP_REPEAT_1_class`. – abergmeier Apr 02 '15 at 10:30
  • @abergmeier Whoops, I swapped the argument order accidentally. Corrected. – Angew is no longer proud of SO Apr 02 '15 at 11:47
  • I had to use `template friend class boost::variant;` instead. Otherwise I had an error about ‘BOOST_PP_REPEAT_1_BOOST_VARIANT_LIMIT_TYPES’ has not been declared – Janek_Kozicki Nov 21 '19 at 00:41
  • @cosurgi Yes, this apparently happens when variadic templates are available to Boost (which was not the case for the original question/answer). I could edit it into my answer, but I think it would be better if you posted it as a new answer, which will be universal. I will then edit mine to add a disclaimer "this is a manual pre-viariadics solution". – Angew is no longer proud of SO Nov 21 '19 at 09:04
1

When variadic templates are available, you would have to use template <BOOST_VARIANT_ENUM_PARAMS(class T)> friend class boost::variant; otherwise you get an error: ‘BOOST_PP_REPEAT_1_BOOST_VARIANT_LIMIT_TYPES’ has not been declared.

Here's a minimal working example:

#include <boost/variant.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>

struct Foo {
public:
    Foo(int){};
private:
    Foo();
    template <BOOST_VARIANT_ENUM_PARAMS(class T)> friend class boost::variant;
    int i;
};

int main()
{
    Foo foo(10);
    boost::variant<Foo> test=foo;
}
Janek_Kozicki
  • 736
  • 7
  • 9