是否存在允许将非const模板实参类型隐式转换为const的规范方法?

Is there a canonical way to allow implicit conversion of a non-const template argument type to a const one?

本文关键字:const 范方法 转换 方法 存在 类型 实参 是否      更新时间:2023-10-16

我有一个函数,它接受特定模板类型的参数;简化的版本可能像这样:

#include <type_traits>
template <typename T>
struct foo
{
    // default constructor
    foo() { }
    // simple copy constructor that can construct a foo<T> from a foo<T>
    // or foo<const T>
    foo(const foo<typename std::remove_const<T>::type> &) { }
};

功能上,foo的行为类似于shared_ptr<T>,具有与此问题无关的其他附加功能。函数的语义决定了它更倾向于接收foo<const T>foo<const T>可以从foo<T>隐式构造,所以我希望能够做以下事情:

template <typename T>
void bar(foo<const T> f) { }
int main()
{
    bar(foo<const int>()); // fine
    bar(foo<int>());       // compile error
}

这失败了,因为没有匹配的bar的重载,foo<int>(即使foo<const int>可以隐式地从foo<int>构造,与模板实例化一致的重载解析似乎比这更严格)。

是否有一个规范的方法来完成这一点?我知道我可以为bar()引入第二个过载,它接受foo<T>并手动调度到bar(foo<const T>),但如果可能的话,我想避免重复。

模板不允许转换!

当你写:

template <typename T>
void bar(foo<const T> f) { }

bar接受任何Tfoo<const T>。它不接受其他任何东西。foo<int>是否可以转换为foo<const int>并不重要,因为这种转换从未被考虑过。句号。

如果您想将收到的f视为const,您可以有条件地将其设置为const:

// convert this version
template <class T> foo<T const> make_const_f(foo<T> const& rhs) { return {rhs}; }    
// pass this version through
template <class T> foo<T const>& make_const_f(foo<T const>& rhs) { return rhs; }
template <typename T>
void bar(foo<T> f) {
    auto&& const_f = make_const_f(f);
    // if T was const, const_f is a reference to f
    // if T wasn't const, const_f is a new object of type foo<T const>
}

代码不工作的原因是隐式转换在模板参数推导之后应用。因此,在这种情况下,foo<int>确实与foo<const T>不匹配,编译器无法推断出T是什么。你可以自己试着直接检查指定的类型:

int main()
{
    bar(foo<const int>()); // fine
    bar<int>(foo<int>());  // also fine
}

你能做的是让编译器接受任何类型:

template <typename T> // T might be foo<int>
void bar(T f) { }

或者,如果您愿意,可以让编译器推导出不带const:

的内部T
template <typename T> // can deduce T as const int
void bar(foo<T> f) { }

如果你真的想执行constness(即使在泛型代码中),你可能想在你的类中添加一个实用函数,就像这样:

foo<const T> as_const() const { return *this; }

所以当你使用泛型函数时,你可以发送const版本的类:

bar<int>(foo<int>{}.as_const());