最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Restricting a Universal Reference to `const T&` or `T&&` - Stack Overflow

programmeradmin2浏览0评论

Is it possible to allow a universal reference argument to be only called as const lvalue-ref or rvalue-ref, but not lvalue-ref?

Consider this:

#include <string>
#include <iostream>

struct S {
    S() {}
    S(const S&) {
        std::cout << "copied" << std::endl;
    }
    S(S&&) {
        std::cout << "moved" << std::endl;
    }    
};

auto foo(auto&& arg) {
    return std::move(arg);
}

int main() {
    std::cout << "temp call" << std::endl;
    foo(S());
    std::cout << std::endl << "variable call" << std::endl;
    S s;
    foo(s);
    std::cout << std::endl << "move call" << std::endl;
    foo(std::move(s));
}

This prints

temp call
moved

variable call
moved            <--- should be copy here

move call
moved

I know that I should use std::forward inside foo when handling universal references:

auto foo(auto&& arg) {
    return std::forward<decltype(arg)>(arg);
}

However, is it possible to enforce already in the signature of foo that an argument ist either const T& or T&&, but never T&?

Is it possible to allow a universal reference argument to be only called as const lvalue-ref or rvalue-ref, but not lvalue-ref?

Consider this:

#include <string>
#include <iostream>

struct S {
    S() {}
    S(const S&) {
        std::cout << "copied" << std::endl;
    }
    S(S&&) {
        std::cout << "moved" << std::endl;
    }    
};

auto foo(auto&& arg) {
    return std::move(arg);
}

int main() {
    std::cout << "temp call" << std::endl;
    foo(S());
    std::cout << std::endl << "variable call" << std::endl;
    S s;
    foo(s);
    std::cout << std::endl << "move call" << std::endl;
    foo(std::move(s));
}

This prints

temp call
moved

variable call
moved            <--- should be copy here

move call
moved

I know that I should use std::forward inside foo when handling universal references:

auto foo(auto&& arg) {
    return std::forward<decltype(arg)>(arg);
}

However, is it possible to enforce already in the signature of foo that an argument ist either const T& or T&&, but never T&?

Share Improve this question asked Mar 28 at 12:58 user3612643user3612643 5,9327 gold badges37 silver badges64 bronze badges 5
  • 1 Any reason you just don't have the function take a const T&? – NathanOliver Commented Mar 28 at 13:03
  • The actual function foo is of course more complicated with a lambda I want to forward the arg into. However, I find using std::forward somewhat error-prone, so I am wondering if I could just use std::move and enforce avoiding lvalue-ref by signature. – user3612643 Commented Mar 28 at 13:06
  • 2 Could you give an example of that error-prone behavior? I've not had issues with forward myself. – NathanOliver Commented Mar 28 at 13:08
  • I am not actually sure if I understand the question. In text you say you want to prevent that the function is called with T& but example and output suggest you want to call it with T& to enable a conditional move. Thats exactly what std::forward was made for. If you encountered issues with std::forward you should better ask about that rather than trying to roll your own. – 463035818_is_not_an_ai Commented Mar 28 at 13:15
  • Side note: you use the c++14 tag but using auto as a parameter is not allowed. – edrezen Commented Mar 28 at 13:16
Add a comment  | 

2 Answers 2

Reset to default 6

You might have overloads,

without template, it is simple, you just have

void foo(Object&&);
void foo(const Object&);

then foo(myObject) would call void foo(const Object&);

With template, it is more complicated, as void foo(auto&&); would match Object& as well.

You might SFINAE it or use requires (C++20)

template <typename T>
requires (!std::is_lvalue_reference_v<T>)
void foo(T&&);

template <typename T>
void foo(const T&);

or forward template function to template class

template <typename T>
struct Foo
{
void operator (T&&); // it is not a forwarding reference, T is fixed by the class already
void operator (const T&);
};

template <typename T>
void foo(T&& t)
{
    Foo<std::decay_t<T>>{}(std::forward<T>(t));
}

or to template function, but with fixed T

// std::type_identity_t to guaranty the usage of foo_impl<Object>(..)
template <typename T>
void foo_impl(std::type_identity_t<T>&&);

template <typename T>
void foo_impl(const std::type_identity_t<T>&);

template <typename T>
void foo(T&& t)
{
    foo_impl<std::decay_t<T>>(std::forward<T>(t));
}

In the presence of an overload that is a better match auto foo(auto&&) cannot be called with a auto&:

auto foo(auto&& arg) { return std::move(arg); }
auto foo(auto& arg) { return arg;}

Live Demo

发布评论

评论列表(0)

  1. 暂无评论