Home > Net >  preventing r-value references in variadic template
preventing r-value references in variadic template

Time:11-01

looking at std::ref and std::cref, the way I think it works is having two prototypes

template< class T >
std::reference_wrapper<const T> cref( const T& t ) noexcept;
template< class T >
void cref( const T&& ) = delete;

and the T&& template function is deleted.

But when I imitate this with a similar variadic template function, the compilation is successful if atleast one argument satisfies the condition. So now I don't understand how or why this (does not?) works.

template<typename ... Ts>
void foo(const Ts& ... ts) { }

template<typename ... Ts>
void foo(const Ts&& ...) = delete;

int main(){
    std::string a{"sss"};
    foo<std::string>(a);
    //foo<std::string>("sss"); error, deleted foo

    foo<std::string, std::string>(a, "sss"); // I was expecting error here
    //foo<std::string, std::string>("aaaa", "sss"); error, deleted foo

    foo<std::string, std::string, std::string>(a, "aaa", "sss"); // I was expecting error here
}

This seems to be the case with clang, gcc and also msvc https://godbolt.org/z/8cboT48En

CodePudding user response:

You can check std::is_lvalue_reference for this purpose.

template<typename ... Ts, std::enable_if_t<(!std::is_lvalue_reference<Ts>::value && ...), int> = 0>
void foo(const Ts& ... ts) {}

Then all of calling function like this is error.

foo<std::string>("sss"); //error, deleted foo

foo<std::string, std::string>(a, "sss"); // I was expecting error here
foo<std::string, std::string>("aaaa", "sss"); error, deleted foo

foo<std::string, std::string, std::string>(a, "aaa", "sss"); // I was expecting error here

CodePudding user response:

So now I don't understand how or why this (does not?) works.

Contrary to reference wrapper code, your overload takes reference to const: temporaries can bind to.

If you remove the const, you have expected error:

template<typename ... Ts> void foo(Ts& ... ts) { /*..*/ }
template<typename ... Ts> void foo(const Ts&& ...) = delete;

Demo

CodePudding user response:

Ordinary string literals are lvalues, so your test isn't testing what you want. Testing with literals that are rvalues, I found you need to have each variant of cv-ref qualifiers.

#include<iostream>
#include<utility>
#include<string>

template<typename ... Ts>
void foo(const Ts& ... ts) { }

template<typename ... Ts>
void foo(Ts& ... ts) { foo(std::as_const(ts)...); }

template<typename ... Ts>
void foo(const Ts&& ...) = delete;

template<typename ... Ts>
void foo(Ts&& ...) = delete;

using namespace std::string_literals;

int main(){
    std::string a{"sss"};
    foo(a);

    // errors, as desired
    // foo("sss"s); 
    // foo(a, "sss"s);
    // foo("aaaa"s, "sss"s);
    // foo(a, "aaa"s, "sss"s);
}

See it live

  • Related