C++ 使用模板参数来解析重载
C++ using a template argument to resolve an overload
我正在编写一个包装器模板 class,它可以包装任意类型并为其注入一些额外的语义,但我不知道如何使重载解析正常工作。当编译器无法推断出通常通过比较竞争转换序列的等级来解决的转换时,就会出现问题,因为所讨论的类型是模板参数,而不是函数参数。例如,
#include <type_traits>
template <typename T> class Wrapper {
T val;
public:
Wrapper() = default;
template <typename U> Wrapper(Wrapper<U> x) : val(x.val) {}
};
void foo(Wrapper<const char *>) {}
void foo(Wrapper<bool>) {}
int main() {
Wrapper<char *> cp;
foo(cp);
}
在这里,对 foo() 的调用不明确。期望的行为是编译器 select void foo(Wrapper<const char *>)
,就像 cp
代替 char *
而 foo
代替 void foo(const char *)
一样.这可能吗?
编辑:感谢大家的快速回复,但也许我应该更清楚。我上面给出的只是一个例子。我需要的是以下问题的通用解决方案:给定任意类型 T
、U
和 V
,假设 C++ 的内置重载解析更喜欢转换 T
-> U
超过 T
-> V
。那么我怎样才能确保 C++ 更喜欢 Wrapper<T>
-> Wrapper<U>
而不是 Wrapper<T>
-> Wrapper<V>
?
我做了这个澄清,因为答案似乎专门针对重载决议的某些方面,比如 cv 限定,而我确实需要一个通用的解决方案。
您在主 char*
前面缺少 const
。
声明如下。它应该工作。
Wrapper<const char *> cp;
下面是测试和结果
你需要让构造函数不那么贪婪。这可以通过 SFINAE:
完成
template <typename T>
using remove_const_from_pointer_t =
std::conditional_t<std::is_pointer<T>::value,
std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>, T>;
template <typename T>
class Wrapper {
T val;
template <typename U>
friend class Wrapper;
public:
Wrapper() = default;
template <
typename U,
std::enable_if_t<
std::is_same<U, remove_const_from_pointer_t<T>>::value, int*> = nullptr>
Wrapper(Wrapper<U> x) : val(x.val) {}
};
您可能想尝试 this 而不是我的 remove_const_from_pointer_t
。
另请注意,我必须添加一个 friend
声明。
编辑: 这在只有一个 void foo(Wrapper<bool>)
过载的情况下不起作用,您必须将 SFINAE 的应用程序从 Wrapper
的构造函数直接到此重载:
template <
typename T,
std::enable_if_t<
std::is_same<std::remove_const_t<T>, char>::value, int*> = nullptr>
void foo(Wrapper<T *>) { }
您可以做的事情很少:
- 干脆禁止从Wrapper
构造Wrapper。这样的东西很容易出错
- 使用 SFINAE
#include <type_traits>
template <typename T> class Wrapper {
T val;
public:
T getVal() const {
return val;
}
Wrapper() = default;
template <typename U,
class = typename std::enable_if<std::is_same<typename std::remove_cv<typename std::remove_pointer<T>::type>::type,
typename std::remove_pointer<U>::type>::value>::type>
Wrapper(Wrapper<U> x) : val(x.getVal()) {}
};
void foo(Wrapper<const char *>) {}
void foo(Wrapper<bool>) {}
int main() {
Wrapper<char *> cp;
foo(cp);
}
使用它你可以只允许特定的一组转换,即:X *-> const X *,整数类型之间的转换等
更新:不幸的是,您似乎无法模仿标准的重载决议规则,因为您只能使用转换运算符,并且在重载决议方面它具有常量等级
这里的问题是,由于模板的原因,两个重载在解析中具有完全相同的权重。
如果要进行重载解析,则必须引入重载解析。
这可以通过添加相应的类型作为第二个(未使用的)参数来完成:
void foo(Wrapper<const char *>, const char *)
void foo(Wrapper<bool>, bool)
借助包装器中的以下别名:
using value_type = T;
下面的foo()函数可以select最好的重载:
template <typename W>
void foo(W && w) {
foo(std::forward<W>(w), typename std::remove_reference_t<W>::value_type{});
}
我正在编写一个包装器模板 class,它可以包装任意类型并为其注入一些额外的语义,但我不知道如何使重载解析正常工作。当编译器无法推断出通常通过比较竞争转换序列的等级来解决的转换时,就会出现问题,因为所讨论的类型是模板参数,而不是函数参数。例如,
#include <type_traits>
template <typename T> class Wrapper {
T val;
public:
Wrapper() = default;
template <typename U> Wrapper(Wrapper<U> x) : val(x.val) {}
};
void foo(Wrapper<const char *>) {}
void foo(Wrapper<bool>) {}
int main() {
Wrapper<char *> cp;
foo(cp);
}
在这里,对 foo() 的调用不明确。期望的行为是编译器 select void foo(Wrapper<const char *>)
,就像 cp
代替 char *
而 foo
代替 void foo(const char *)
一样.这可能吗?
编辑:感谢大家的快速回复,但也许我应该更清楚。我上面给出的只是一个例子。我需要的是以下问题的通用解决方案:给定任意类型 T
、U
和 V
,假设 C++ 的内置重载解析更喜欢转换 T
-> U
超过 T
-> V
。那么我怎样才能确保 C++ 更喜欢 Wrapper<T>
-> Wrapper<U>
而不是 Wrapper<T>
-> Wrapper<V>
?
我做了这个澄清,因为答案似乎专门针对重载决议的某些方面,比如 cv 限定,而我确实需要一个通用的解决方案。
您在主 char*
前面缺少 const
。
声明如下。它应该工作。
Wrapper<const char *> cp;
下面是测试和结果
你需要让构造函数不那么贪婪。这可以通过 SFINAE:
完成template <typename T>
using remove_const_from_pointer_t =
std::conditional_t<std::is_pointer<T>::value,
std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>, T>;
template <typename T>
class Wrapper {
T val;
template <typename U>
friend class Wrapper;
public:
Wrapper() = default;
template <
typename U,
std::enable_if_t<
std::is_same<U, remove_const_from_pointer_t<T>>::value, int*> = nullptr>
Wrapper(Wrapper<U> x) : val(x.val) {}
};
您可能想尝试 this 而不是我的 remove_const_from_pointer_t
。
另请注意,我必须添加一个 friend
声明。
编辑: 这在只有一个 void foo(Wrapper<bool>)
过载的情况下不起作用,您必须将 SFINAE 的应用程序从 Wrapper
的构造函数直接到此重载:
template <
typename T,
std::enable_if_t<
std::is_same<std::remove_const_t<T>, char>::value, int*> = nullptr>
void foo(Wrapper<T *>) { }
您可以做的事情很少:
- 干脆禁止从Wrapper
构造Wrapper 。这样的东西很容易出错 - 使用 SFINAE
#include <type_traits>
template <typename T> class Wrapper {
T val;
public:
T getVal() const {
return val;
}
Wrapper() = default;
template <typename U,
class = typename std::enable_if<std::is_same<typename std::remove_cv<typename std::remove_pointer<T>::type>::type,
typename std::remove_pointer<U>::type>::value>::type>
Wrapper(Wrapper<U> x) : val(x.getVal()) {}
};
void foo(Wrapper<const char *>) {}
void foo(Wrapper<bool>) {}
int main() {
Wrapper<char *> cp;
foo(cp);
}
使用它你可以只允许特定的一组转换,即:X *-> const X *,整数类型之间的转换等
更新:不幸的是,您似乎无法模仿标准的重载决议规则,因为您只能使用转换运算符,并且在重载决议方面它具有常量等级
这里的问题是,由于模板的原因,两个重载在解析中具有完全相同的权重。
如果要进行重载解析,则必须引入重载解析。
这可以通过添加相应的类型作为第二个(未使用的)参数来完成:
void foo(Wrapper<const char *>, const char *)
void foo(Wrapper<bool>, bool)
借助包装器中的以下别名:
using value_type = T;
下面的foo()函数可以select最好的重载:
template <typename W>
void foo(W && w) {
foo(std::forward<W>(w), typename std::remove_reference_t<W>::value_type{});
}