1

I'm writing a class that has an explicit constructor taking a const char* argument. For the intents and purposes of this question it looks like this:

struct Symbol
{
    Symbol()=default;
    explicit Symbol(const char*);
};

Now I want to write an example for documentation purposes that initializes an array (array/vector/list - I don't care about the exact type) and I need the example to be as clear and concise as possible. Ideally it would look like this:

Symbol symbols[] = { "a", "b", "c"};

That does not compile because of the explicit keyword and I am not prepared to make the constructor implicit.

How can I make this work, with the focus of making the example code as expressive as possible?

EDIT: I went for Bolov's solution with a little help from Caleth:

struct Symbol
{
    Symbol();
    explicit Symbol(const char*);

    template <class... Args> 
    static std::array<Symbol, sizeof...(Args)> Array(Args... args)
    {
        return {Symbol{args}...}; 
    } 
};

int main()
{
    auto symbols = Symbol::Array("a", "b", "c");
}
bgp2000
  • 1,070
  • 13
  • 32

2 Answers2

5

Well, your constructor is explicit so you need to use it as such:

Symbol symbols[] = {Symbol{"a"}, Symbol{"b"}, Symbol{"c"}};

Both gcc and clang both the copy/move constructor and since C++17 that is the required behavior so there is no performance overhead.


If you really want to keep the constructor explicit and be able to create an array without explicitly stating it for every element then you can create a helper function:

template <class... Args,
          class Enable = std::enable_if_t<(... && std::is_same_v<Args, const char*>)>>
auto make_symbols(Args... args) -> std::array<Symbol, sizeof...(Args)>
{
    return {Symbol{args}...};
}

and use it like this:

auto symbols = make_symbols("a", "b", "c");

Again the move/copies are entirely elided.

The make_symbols function uses C++17 features for checking the arguments types. If you need the constraint for a previous standard version (including C++11) see this answer Restrict variadic template arguments. Or, depending on your needs, removing the check can also be a choice.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • I knew I should have mentioned the obvious solution... That doesn't scale very well. – bgp2000 Feb 12 '18 at 09:12
  • 1
    what do you mean by "it doesn't scale very well"? – bolov Feb 12 '18 at 09:14
  • For every variable, you have to repeat the class name. This makes it less concise than I'd like it to be. – bgp2000 Feb 12 '18 at 09:16
  • @bgp2000 Do the constructor *have* to be `explicit`? – Some programmer dude Feb 12 '18 at 09:19
  • @Someprogrammerdude 'fraid so. – bgp2000 Feb 12 '18 at 09:19
  • @bolov Can you post make_symbols() as a separate answer? That looks promising. – bgp2000 Feb 12 '18 at 09:30
  • Why do you need it as a separate answer? I don't see any benefit for splitting the answer... – bolov Feb 12 '18 at 09:32
  • Ok, thanks for the link to your other great post! This is what I need, but I cannot use C++17 and I can't seem to adapt the C++11 version to my use case of an explicit constructor taking a const char*. See https://godbolt.org/g/4s3ac6. – bgp2000 Feb 12 '18 at 10:08
  • 1
    I'm not convinced that you need the argument checking here. Anything passing non-symbols fails to instantiate the template. `template std::array make_symbols(Args... args) { return {Symbol{args}...}; }` – Caleth Feb 12 '18 at 10:32
  • @Caleth maybe he has other constructors he doesn't want to kick in here. I don't know. It's an option if he wants. – bolov Feb 12 '18 at 11:04
  • @bgp2000 have you tried? because in the link I see only code from the original post, no attempt to modify it to your needs. Anyway here it is: https://godbolt.org/g/BCc7E8 – bolov Feb 12 '18 at 11:08
  • I did try it. Thanks for the working code! I think @Caleth's version is what I need. It doesn't clutter up my header as much and catches errors like `make_symbols(false)`. – bgp2000 Feb 12 '18 at 14:00
1

The best I've come up with so far is:

std::vector<Symbol> symbols;
for(auto v: { "a", "b", "c"})
    symbols.emplace_back(v);
bgp2000
  • 1,070
  • 13
  • 32