C++折叠表达式替换特定类型转发时自定义重载未参与决议问题
折叠表达式中特定类型定制转发重载不生效问题
尝试在折叠表达式中对特定类型做定制替换处理,其余类型直接执行转发逻辑,但始终未能实现预期效果。
通用转发函数模拟实现
首先复制GCC标准库中std::forward的实现,仅添加输出语句用于观察调用流程,代码放置在test命名空间下:
namespace test { template<typename T> constexpr T&& fw(typename std::remove_reference<T>::type& t) noexcept { std::cout << "standard" << std::endl; return static_cast<T&&>(t); } template<typename T> constexpr T&& fw(typename std::remove_reference<T>::type&& t) noexcept { std::cout << "standard (r-value)" << std::endl; static_assert ( !std::is_lvalue_reference<T>::value, "template argument substituting T is an lvalue reference type" ); return static_cast<T&&>(t); } }
针对std::string的定制重载尝试
由于std::forward调用时需要显式指定模板实参,以std::string为例编写了一组针对特定类型的fw模板重载:
template<typename> auto fw(std::string const& s) { std::cout << "specialised (const reference): " << s << std::endl; return s; } template<typename> auto fw(std::string& s) { std::cout << "specialised (reference): " << s << std::endl; return s; } template<typename> std::string&& fw(std::string&& s) { std::cout << "specialised (r-value reference): " << s << std::endl; return std::move(s); }
尝试过程中逐个添加过所有相关重载,运行结果始终没有变化;即使出现调用歧义也可以通过移除部分重载解决,将这些重载放置在独立命名空间中也没有改变运行结果。
测试代码
原本计划将上述函数设计为类的静态私有成员,测试代码如下:
class Test { public: template <typename ... T> void test(T&& ... t) { using t::fw; using test::fw; ( g(fw<T>(t)), ... ); } private: template <typename T> T&& g(T&& t) { std::cout << "g: r-value: " << t << '\n' << std::endl; return std::move(t); } template <typename T> T& g(T& t) { std::cout << "g: l-value " << t << '\n' << std::endl; return t; } }; int main() { int nn = 10; Test t; std::string s("daal"); t.test(12, nn, std::string("alda"), s); return 0; }
运行结果
程序实际输出如下:
standard g: r-value: 12 standard g: l-value 10 standard g: r-value: alda standard g: l-value daal
所有调用都命中了通用版本的fw,针对std::string编写的重载始终没有被纳入重载决议候选集。尝试通过using声明同时引入不同命名空间下的fw函数:仅引入全局命名空间的fw可以正常完成普通转发,但无法实现特定类型的替换逻辑,调整using声明的顺序也不会影响结果。
补充排查信息
- 后续排查发现,模板实例化后,自定义重载和基础版本的fw函数签名实际是相同的,如果这些重载真的被纳入候选集,理论上应该会触发二义性调用错误,但实际并未出现该错误。
- 曾尝试在
test命名空间内为std::string提供fw的模板特化,同样失败;且该方案本身存在缺陷:如果在头文件中暴露该特化,会导致所有引入该头文件的位置的string类型转发逻辑都被替换,不符合预期作用域要求。 - 目前实际业务场景不需要使用可变参模板,可以直接在函数内使用
constexpr if实现分支逻辑,本次提问仅出于技术好奇心探究原理,不存在XY问题。
内容的提问来源于stack exchange,提问作者Aconcagua
相关产品推荐
相关产品推荐

