1

How can I explicitly instantiate a variadic function while calling it ?
The following code doesn't work and the compiler says that it couldn't convert the first parameter to a string &&.

#include <iostream>

using namespace std;

template<typename ... Args>
void variadic( Args &&... args );

int main()
{
    string str;
    variadic<string &&, string &&>( str, ref( str ) );
}

template<typename ... Args>
void variadic( Args &&... args )
{
    ((cout << (void*)&args << endl), ...);
}

I have some code where the function object itself is a templated parameter which is invoked with some variadic parameters so that it has to be specialized before. Of course I could use a lambda as this parameter but maybe there's a simpler way.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • 1
    please include the complete error message verbatim in the question. When i try to compile the error is not about a missing overload: https://godbolt.org/z/hqs5xn3PM – 463035818_is_not_an_ai Oct 17 '22 at 07:17

2 Answers2

3

Let the compiler deduce the arguments, and print them:

#include <iostream>

using namespace std;

template<typename ... Args>
void variadic( Args &&... args );

int main()
{
    string str;
    variadic( str, ref( str ) );
}

template<typename ... Args>
void variadic( Args &&... args )
{
    #ifdef _MSC_VER
    std::cout << __FUNCSIG__ << '\n';
    #else
    std::cout << __PRETTY_FUNCTION__ << '\n';
    #endif
    ((cout << (void*)&args << endl), ...);
}

This gives me:

void variadic(Args &&...) [Args = <std::basic_string<char> &, std::reference_wrapper<std::basic_string<char>>>]

Shorten std::basic_string<char> to std::string, and you get:

variadic<std::string &, std::reference_wrapper<std::string>>( str, ref( str ) );

Why do we see those?

Firstly, when you don't specify template arguments here, args acts as a forwarding reference. When receiving lvalues, those deduce the underlying template parameter to an lvalue reference, so std::string & instead of std::string (the latter would be used for an rvalue).

Secondly, std::ref() returns std::reference_wrapper<...>, so it's wrong to use std::string directly.


Also: this is not an "explicit instantiation" (that would be template void variadic<...>(...);). You're just specifying template arguments explicitly.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
2

Your explicit template arguments specification is not the issue here.

The problem is that neither str, nor std::ref(str) are std::string R-values (which is what std::string && mentioned in the template instantiation is).

You need to use std::move(str) to make str compatible with the function parameter of type std::string &&:

string str;
variadic<string&&>(std::move(str));

A side note: better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • The arguments don't have to be rvalue references (and can't be, because expressions never have reference types). They just have to be rvalues of the right type. – HolyBlackCat Oct 17 '22 at 07:23
  • @HolyBlackCat I didn't get your point. Feel free to edit my question if something is inaccurate (or please explain in a comment). – wohlstad Oct 17 '22 at 07:25
  • `void foo(std::string &&); foo(std::string("42"));` - this compiles, even though `std::string("42")` is not an rvalue reference. My point is that instead of *"The problem is that [...] are not rvalue references"*, you should say *"are not rvalues"*. – HolyBlackCat Oct 17 '22 at 07:28
  • Now I got it. Of course you are right and what I actually had in mind originally. Anyway - fixed. – wohlstad Oct 17 '22 at 07:29