【问题标题】:why can't I pass an l-value to a template class constructor that uses universal references?为什么我不能将左值传递给使用通用引用的模板类构造函数?
【发布时间】:2022-10-20 20:59:32
【问题描述】:

我有一个模板类MyClass<T>,它在其构造函数中采用一些包含整数的可迭代(例如T = std::vector<int>)并对其进行处理。

我希望能够将可迭代对象作为临时对象(例如MyClass(std::vector<int>{3,6,9}) 或类似的右值参数)或命名变量(导致左值作为构造函数参数)传递。

我想使用 C++17 模板类推理(即写MyClass(...),而不是MyClass<std::vector<int>>(...))。

我认为我可以将构造函数参数声明为MyClass(T && vec)(“通用引用”)以获取左值或右值(就像我可以使用函数一样),但它给出了错误。似乎T 总是被推断为std::vector<int> 而永远不会std::vector<int>& 与类,而当参数是左值时函数推断std::vector<int>&

模板构造函数推理和模板函数推理的规则究竟有何不同?我可以避免仅仅为了模板推断而使用包装函数(例如myFunction(T&&vec) { return MyClass<T>(std::forward<T>(vec)); })吗?

Godbolt 上运行以下代码:

#include <iostream>
#include <utility>
#include <vector>

template <typename T>
using BeginType = decltype(std::declval<T>().begin());

template <typename T>
struct MyClass {
    BeginType<T> begin;
    BeginType<T> end;
    MyClass(T && vec) {
        begin = std::forward<T>(vec).begin();
        end = std::forward<T>(vec).end();
    }
    int sum() {
        int sum = 0;
        for (auto it = begin; it != end; ++it) sum += *it;
        return sum;
    }
};

template <typename T>
MyClass<T> myFunction(T && vec) {
    return MyClass<T>(std::forward<T>(vec));
}

int main() {
    std::vector<int> x{1, 2, 3};
    std::vector<int> y{2, 4, 6};

    // Warmup: Passing r-values works fine
    std::cout << MyClass(std::vector<int>{3, 6, 9}).sum() << std::endl;  // works fine: T is std::vector<int>
    std::cout << MyClass(std::move(y)).sum() << std::endl;  // works fine: T is std::vector<int>

    // Unexpected: Passing l-values doesn't work
    // std::cout << MyClass(x).sum() << std::endl;  // error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'

    // Compare: Passing l-values to function works fine
    std::cout << myFunction(x).sum() << std::endl;  // works fine: T is std::vector<int>&
}

【问题讨论】:

    标签: c++17


    【解决方案1】:

    在类定义后添加user-defined deduction guide

    template <typename T>
    struct MyClass {
        // same as in question
    };
    template <typename TT> MyClass(TT && vec) -> MyClass<TT>;
    

    另见How to write a constructor for a template class using universal reference arguments in C++

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-19
      相关资源
      最近更新 更多