标记调度、可变模板、通用引用和遗漏的常量说明符
Tag dispatching, variadic template, universal reference and the missed const specifier
请考虑以下示例(标签调度、可变模板、完美转发等,一体化):
#include <iostream>
#include <utility>
#include <string>
struct A { };
struct B { };
void doIt(A&&, const std::string &) {
std::cout << "A-spec" << std::endl;
}
template<typename T, typename... Args>
void doIt(T&&, Args&&...) {
std::cout << "template" << std::endl;
}
template<typename T, typename... Args>
void fn(Args&&... args) {
doIt(T{}, std::forward<Args>(args)...);
}
int main() {
const std::string foo = "foo";
std::string bar = "bar";
fn<A>(foo);
fn<A>(bar);
fn<B>(foo);
}
在这种情况下,输出为:
A-spec
template
template
原因很明显,这并不困扰我。
我想要实现的是在这两种情况下都调用doIt
函数的第一个实例,无论字符串是否具有const说明符
当然,一个可能的解决方案是定义一个具有正确原型的新doIt
,无论如何,我想知道是否还有其他解决方案
到目前为止,我已经尝试过通过add_const
获得它,但我确信我错过了一些东西
有没有可行的解决方案可以无声地添加const说明符并使其工作?
编辑
我已经更新了上面的例子,以便与实际问题更加一致
此外,尽管答案很有趣,但我忘了引用这只是一个简化的例子,所以真正的问题不仅仅涉及std::string
。相反,可能发生的情况是(例如)对于标记A
,参数是int
和const std::string &
,而对于标记B
,参数是类C
的实例float
,依此类推。
正因为如此,那些试图以某种方式使用std::string
类型来解决问题的答案并不能解决真正的问题,我很抱歉。
引入两个独立的函数,这样它们就不会相互冲突,编译器也不会产生任何歧义错误:
void doIt(A&&, const std::string &)
{
std::cout << "A-spec" << std::endl;
}
template <typename T, typename... Args>
void doIt_template(T&&, Args&&...)
{
std::cout << "template" << std::endl;
}
优先考虑两个额外的过载;首选的方法是尝试调用目标函数的专用、非模板化版本:
template <typename T, typename... Args>
auto fn_impl(int, Args&&... args)
-> decltype(doIt(T{}, std::forward<Args>(args)...), void())
{
doIt(T{}, std::forward<Args>(args)...);
}
template <typename T, typename... Args>
void fn_impl(char, Args&&... args)
{
doIt_template(T{}, std::forward<Args>(args)...);
}
引入单一的通用调度员:
template <typename T, typename... Args>
void fn(Args&&... args)
{
fn_impl<T>(0, std::forward<Args>(args)...);
}
DEMO
转发引用的过载是一种痛苦,因为它们非常贪婪。
如果Args...
是可转换为std::string
:的单个参数,则一种选择是禁用模板版本
template<typename... Args>
struct convertible_to_single_string {
static std::true_type help (std::string);
static std::false_type help (...);
using type = decltype(help(std::declval<Args>()...));
static constexpr auto value = type::value;
};
template<typename... Args>
std::enable_if_t<!convertible_to_single_string<Args...>::value, void>
doIt(int, Args&&...) {
std::cout << "template" << std::endl;
}
这也将调用const char*
的字符串版本或任何具有到std::string
的用户定义转换的版本。如果你不想这样,还有其他的可能性,比如从类型中删除所有引用和cv限定符,并检查它是否为std::is_same<std::string, T>
。
实时演示
template<class String,
std::enable_if_t<std::is_same<std::decay_t<String>,std::string>{},int> =0
>
void doIt(int, String&&) {
std::cout << "std::string" << std::endl;
}
这使用一个转发引用,然后SFINAE只获取其中的任何string
类型。
如果您愿意将args绑定到一个元组中,那么您也可以执行using标记调度。is_same
可以替换为is_convertible
,然后可以使用实际转换为std::string
的具有不同名称的辅助函数。
另一种方法是为标签保留第二层中的第一个arg,然后手动计算出您想要的重载,并生成该标签类型。
- 激励'inline'说明符的真实世界示例?
- #定义c-预处理器常量..我做错了什么
- 用C++中的一个变量定义一个常量
- 什么时候在C++中返回常量引用是个好主意
- 代理对象的常量正确性
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 通过多个头文件使用常量变量
- 在cuda线程之间共享大量常量数据
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 缺少类型说明符,显式类型为"缺少错误"
- 是默认情况下分配给char数组常量的值
- 私有类型的静态常量成员
- 类似枚举的计算常量
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 为什么我可以通过引用修改常量返回
- 如何创建长度由常量参数指定的数组
- 为什么自动说明符从 &指针推断出"顶部和低"级别的常量?
- 可以通过常量说明符重载功能
- 标记调度、可变模板、通用引用和遗漏的常量说明符