考虑以下程序:
#include <cstddef>
#include <cstdio>
void f(char const*&&) { std::puts("char const*&&"); } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)
template <std::size_t N>
void f(char const (&)[N]) { std::puts("char const(&)[N]"); } // (3)
int main()
{
const char data[] = "a";
f(data);
}
Run Code Online (Sandbox Code Playgroud)
哪个f应该叫?为什么?
三个编译器的最新发布版本不同意这个问题的答案:
在不同的C++ 0x草案中,重载决策规则是否发生了很大变化?或者,这两个编译器真的完全错了吗?哪个重载是根据最新的C++ 0x草案选择的正确重载?
在下面的示例中,0它以一种特殊的方式运行:它选择与示例函数调用所期望的不同的重载。我想知道为什么。我的理解也如下。
#include <iostream>
template<typename T>
void f(T a) {
std::cout << "first" << std::endl;
}
template<typename T>
void f(T* a) {
std::cout << "second" << std::endl;
}
int main()
{
f(0);
f<size_t>(0);
f<size_t>(0UL);
f(1);
f<size_t>(1);
}
Run Code Online (Sandbox Code Playgroud)
输出:
first
second
first
first
first
Run Code Online (Sandbox Code Playgroud)
我的理解:
f(0)- 模板参数推导,整数文字0是int类型,因此f选择第一个T=int
f<size_t>(0)-带有整数提升的显式模板实例化,选择的类型是T=size_t,选择第一个函数并从到提升0(我在这里错了)intsize_t
f<size_t>(0UL)- 与上面相同,但没有升级(0 已经是 type size_t)
f(1)- 与 …
c++ templates function-templates overload-resolution template-argument-deduction
我试图理解为什么有人会编写一个带有const右值引用的函数.
在下面的代码示例中,const rvalue引用函数的用途是什么(返回"3").为什么重载决策首选const Rvalue高于const LValue引用函数(返回"2").
#include <string>
#include <vector>
#include <iostream>
std::vector<std::string> createVector() { return std::vector<std::string>(); }
//takes movable rvalue
void func(std::vector<std::string> &&p) { std::cout << "1"; }
//takes const lvalue
void func(const std::vector<std::string> &p) { std::cout << "2"; }
//takes const rvalue???
//what is the point of const rvalue? if const I assume it is not movable?
void func(const std::vector<std::string> &&p) { std::cout << "3"; }
int main()
{
func(createVector());
return 0;
}
Run Code Online (Sandbox Code Playgroud) 考虑以下代码。
struct any
{
template <typename T>
operator T &&() const;
template <typename T>
operator T &() const;
};
int main()
{
int a = any{};
}
Run Code Online (Sandbox Code Playgroud)
这里第二个转换运算符由重载决议选择。为什么?
据我了解,这两个运算符分别推导为operator int &&() const和operator int &() const。两者都在可行的功能集中。通读 [over.match.best] 并没有帮助我弄清楚为什么后者更好。
为什么后者的功能比前者好?
我正在使用 C++ 库 ( strf ),其中的某处具有以下代码:
namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }
template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)
现在,我想strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)在我的代码中使用。但是如果我这样做,我会收到以下错误(使用 CUDA 10.1 的 NVCC):
error: more than one instance of overloaded function "strf::range" matches the argument list:
function template "auto strf::range(ForwardIt, ForwardIt)"
function template "auto strf::range(const Range &, const CharT *)" …Run Code Online (Sandbox Code Playgroud) c++ nvcc overload-resolution c++11 template-argument-deduction
struct A{
operator char() const{ return 'a'; }
operator int() const{ return 10; }
};
struct B {
void operator<< (int) { }
};
int main()
{
A a;
B b;
b << a;
}
Run Code Online (Sandbox Code Playgroud)
ato 的转换int可以是via,a.operator char()然后是整数提升,也可以是a.operator int()身份转换(即根本没有转换).标准说(§13.3.3.1[over.best.ics]/p10,脚注省略,加粗我的;所有引用均来自N3936):
如果存在几个不同的转换序列,每个转换序列将参数转换为参数类型,则与参数相关联的隐式转换序列被定义为指定为模糊转换序列的唯一转换序列.为了按照13.3.3.2中的描述对隐式转换序列进行排序,模糊转换序列被视为用户定义的序列,与任何其他用户定义的转换序列无法区分.如果选择使用模糊转换序列的函数作为最佳可行函数,则调用将是错误的,因为调用中的一个参数的转换是不明确的.
在这里,B::operator<<(int)是唯一可行的候选者 - 因此是最好的可行候选者,即使参数的转换序列是不明确的转换序列.然后,根据粗体句子,调用应该是格式错误的,因为"调用中的一个参数的转换是模糊的".
然而,我测试的编译器(g ++,clang和MSVC)实际上没有报告错误,这是有道理的,因为在通过重载决定选择调用函数之后,函数的"参数(8.3.5)应该被初始化(8.5,12.8) ,12.1)及其对应的参数"(§5.2.2[expr.call]/p4).这个初始化是复制初始化(§8.5[dcl.init]/p15),根据§8.5[dcl.init]/p17,导致新一轮的重载决策,以确定要使用的转换函数:
初始化器的语义如下.的目标类型 是对象或引用的类型被初始化和 源类型是初始化表达式的类型.如果初始化程序不是单个(可能带括号的)表达式,则不定义源类型.
- [...]
- 如果目标类型是(可能是cv限定的)类类型:[...]
- 否则,如果源类型是(可能是cv限定的)类类型,则考虑转换函数.列举了适用的转换函数(13.3.1.5),并通过重载决策(13.3)选择最佳函数.调用如此选择的用户定义转换以将初始化表达式转换为正在初始化的对象.如果转换不能完成或不明确,则初始化是错误的.
- [...] …
我正在玩一个用C++重载lambdas的技巧.特别:
// For std::function
#include <functional>
// For std::string
#include <string>
// For std::cout
#include <iostream>
template <class... F>
struct overload : F... {
overload(F... f) : F(f)... {}
};
template <class... F>
auto make_overload(F... f) {
return overload<F...>(f...);
}
int main() {
std::function <int(int,int)> f = [](int x,int y) {
return x+y;
};
std::function <double(double,double)> g = [](double x,double y) {
return x+y;
};
std::function <std::string(std::string,std::string)> h = [](std::string x,std::string y) {
return x+y;
};
auto fgh = make_overload(f,g,h); …Run Code Online (Sandbox Code Playgroud) 在回答有关使用枚举的重载解析的问题时,出现了此问题.
虽然案例long long肯定是MSVC2012NovCTP中的一个错误(根据标准文本和gcc 4.7.1的测试),我无法弄清楚为什么会出现以下行为:
#include <iostream>
enum charEnum : char { A = 'A' };
void fct(char) { std::cout << "fct(char)" << std::endl; }
void fct(int) { std::cout << "fct(int)" << std::endl; }
void fct(long long) { std::cout << "fct(long long)" << std::endl; }
int main()
{
fct('A');
fct(A);
}
Run Code Online (Sandbox Code Playgroud)
MSVC2012NovCTP和gcc 4.7.1都同意此输出:
fct(char)
fct(int)
不A应该转换charEnum为char?为什么A被转换成int?
编辑:clang抱怨说这个电话含糊不清,这与我在下面的解释一致; 那说,如果它只被认为是潜在的类型,我仍然会发现它更直观.
两个相关的标准摘录是§7.2/ 9:
枚举器的值或未范围的枚举类型的对象通过整数提升转换为整数(4.5)
并且§4.5/ 4:
其底层类型为固定(7.2)的无范围枚举类型的prvalue可以转换为其基础类型的prvalue.此外,如果可以对其基础类型应用整数提升,则其基础类型固定的未范围枚举类型的prvalue也可以转换为提升的基础类型的prvalue.
因此, …
考虑以下:
struct A {
A(float ) { }
A(int ) { }
};
int main() {
A{1.1}; // error: ambiguous
}
Run Code Online (Sandbox Code Playgroud)
这无法编译时出现关于模糊重载的错误A::A.两位候选人都被认为是可行的,因为要求很简单:
第二,为了
F成为一个可行的函数,每个参数都应该存在一个隐式转换序列(13.3.3.1),它将该参数转换为相应的参数F.
虽然存在从隐式转换序列double到int,该A(int )过载不是实际可行的(在规范,非C++ -标准的意义上) -这将涉及一个收缩转换,因此是形成不良.
为什么在确定可行的候选人的过程中不考虑缩小转换?尽管只有一个候选人可行,但是有没有其他情况下过载被认为是模棱两可的?
你能猜出这个简单程序的输出吗?
\n#include <vector>\n#include <string>\n#include <exception>\n#include <iostream>\n\nint main()\n{\n try { \n struct X {\n explicit X(int) {}\n X(std::string) {} // Just to confuse you more...\n };\n std::vector<X>{"a", "b"};\n } catch (std::exception& x) {\n std::cerr << x.what();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n好吧,我不能,这花了我一天的时间“研究”,然后才登陆这里,最终从一些复杂的现实生活代码中提取出来(到处都是类型别名,匿名。与非 POD 成员的联合以及手工 -精心策划的演员/导演等,只是为了营造氛围)。
\n而且……我还是看不出发生了什么!有人可以给一个温柔的提示吗?(希望只是一个盲点。我不再专业地从事 C++ 工作。)
\n注意:使用(最新的)MSVC/W4和 GCC进行干净编译* -Wall;两者的输出相同(语义上)。
* 即使没有“迷惑读者”这句话。我想我会做噩梦。
\n(请耐心等待我尽量不将所有内容拼写得更多 \xe2\x80\x94 毕竟,这确实是不言自明的,对吧?除了,对我来说完全相反......)
\n