Search code examples
c++templatesvariadic-templates

preventing r-value references in variadic template


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


Solution

  • 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