可以接收任何 T、T& 和 T&& 作为输入的函数,并且还可以识别其类型?

Function that can receive any of T, T&, and T&& as input, AND also recognize its type?

提问人:cppBeginner 提问时间:8/10/2023 最后编辑:cppBeginner 更新时间:8/16/2023 访问量:137

问:

我想创建一个正确识别其参数类型的函数。

template<class T> void test(T&& t){
    if constexpr(std::is_same_v<T,int>){
        std::cout<< "= int"<<std::endl;
    }
    if constexpr(std::is_same_v<T,const int&>){
        std::cout<< "= const int&"<<std::endl;
    }
    if constexpr(std::is_same_v<T,int&>){
        std::cout<< "= int&"<<std::endl;
    }
    if constexpr(std::is_same_v<T,int&&>){
        std::cout<< "= int&&"<<std::endl;
    }
    if constexpr(std::is_same_v<T,std::nullptr_t>){
        std::cout<< "= std::nullptr_t"<<std::endl;
    }
}

int funcReturnInt(){return 5;}
    
int main(){
    const int a=0;
    test(funcReturnInt()); // int  (correct)
    test(a);        // const int&   (correct)
    test(nullptr);  // std::nullptr_t  (correct)
    int s=0;
    test(std::move(s)); // print "int" (not "int&&") , I am sad
}

如何创建函数,以便它可以在每种情况下正确识别其单个参数的类型?test()

我是新手,所以我阅读了一些关于右值引用(123)的指南,但它们没有描述如何创建这样一个完全感知的通配符函数,例如 。T&&test<T>(T??)

我稍后将应用该解决方案。test() requires ... {}

我宁愿避免重载,因为我喜欢将代码压缩在 1 个地方(也用于教育目的)。

编辑 :: 非常有用的评论指出这通常是不可能的。为什么我的代码正确检查其他情况,但 C++ 会冒昧地摆脱我的?请将其作为解决方案发布。&&

我仍然怀疑为什么 in 变得正确,但在 成为.int&main()T=int&int&&main()T=int

c++ rvalue-引用 if-constexpr

评论

1赞 Some programmer dude 8/10/2023
您需要解决的根本问题是什么?为什么需要“正确识别参数类型”?这将如何解决根本问题?如果您直接询问实际的、真实的和潜在的问题,也许我们可以更好地帮助您?另外,请阅读有关XY问题的信息,因为目前您的问题是一个。
2赞 HolyBlackCat 8/10/2023
我一般认为,你无法检测到像宏这样的东西和没有宏之间的区别,这些宏会在内部使用。T &&Tdecltype
2赞 Swift - Friday Pie 8/10/2023
@cppBeginner如果不为每种类型设计它,这是不可能的。但根据用例的不同,有多种调度模式。访问者的重载和类型擦除是最常见的。
3赞 HolyBlackCat 8/10/2023
很难证明是否定的。standard 和 cppreference 通常会列出可能的事情,它们没有明确说某某是不可能的。
1赞 HolyBlackCat 8/10/2023
@DanielLangr 两者都将与您的代码一起打印,但第一个是 prvalue,第二个是 xvalue。我告诉 OP 您无法单独检测模板的差异。test(42);test(std::move(s))int &&

答:

2赞 dfrib 8/15/2023 #1

强调我的:

我是新手,所以我阅读了一些关于右值引用(1、2、3)的指南,但它们没有描述如何创建这样一个完全感知的通配符函数,例如 。T&&test<T>(T??)

在此声明中:

template<class T> void test(T&& t)

虽然经常被误认为是右值引用,但它不是右值引用,而是转发引用(有时称为通用引用)。要理解转发引用,我们需要了解的不仅仅是右值引用,例如引用折叠规则和完美转发。有关详细信息,请参阅以下问答:t

我仍然怀疑为什么在 main() 中变得正确,但在 中变成了 'T=int。int&T=int&int&&main()

在通话中

test(std::move(s));

参数类型 A 是 ,完整的参数类型是 表示推导的 是int&&int&&Tint

test<int>(int&& t);
     ^^^  ^^^^^ parameter-type (T&& -> int&&)
       \ deduced T

有关详细信息,请参阅例如 [temp.deduct.call]/3

当参数类型为左值时,通用引用尤其应用引用折叠规则。对于调用,参数类型是 ,但出于演绎的目的,用于就地,推导到 ,其中生成的函数参数类型从 折叠到 ( -> )。test(a)int constint const&Tint const&T&&int const& &&int const&&& &&

评论

0赞 Daniel Langr 8/16/2023
“调用 test(std::move(s)) 导致 rvalue 引用到右值引用”。你确定这是正确的吗?IIRC,这里将模板参数推导出为 only,然后替换为 。因此,该参数是直接的右值引用,不应用任何折叠规则。现场演示:godbolt.org/z/M3W8baKanintTt
1赞 dfrib 8/16/2023
@DanielLangr 您的演示产生的结果与我自己的(错误的)论点相同(表明推导确实如此),但是是的,您是正确的,引用折叠规则在这里无关紧要。对于类型和转发引用作为函数参数的参数,简单地推导出为 。而它仅适用于参数类型为左值的情况,例如,引用折叠规则适用。谢谢。Tintint&&TT&&Tintint&