首先,考虑一下这个C++代码:
#include <stdio.h>
struct foo_int {
void print(int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void print(const char* x) {
printf("str %s\n", x);
}
};
struct foo : foo_int, foo_str {
//using foo_int::print;
//using foo_str::print;
};
int main() {
foo f;
f.print(123);
f.print("abc");
}
Run Code Online (Sandbox Code Playgroud)
正如根据标准所预期的那样,这无法编译,因为print为了重载解析的目的在每个基类中单独考虑,因此调用是不明确的.这是Clang(4.0),gcc(6.3)和MSVC(17.0)的情况 - 请参阅此处的 godbolt结果.
现在考虑以下片段,唯一的区别是我们使用operator()而不是print:
#include <stdio.h>
struct foo_int {
void operator() (int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void operator() (const …Run Code Online (Sandbox Code Playgroud) c++ lambda multiple-inheritance language-lawyer overload-resolution
在C++ 0x中,简化了SFINAE规则,使得在演绎的"直接上下文"中出现的任何无效表达式或类型不会导致编译器错误,而是导致演绎失败(SFINAE).
我的问题是:
如果我采用重载函数的地址并且无法解决,那么在演绎的直接上下文中是否会失败?
(如果它无法解决,那么它是一个硬错误还是SFINAE)?
以下是一些示例代码:
struct X
{
// template<class T> T* foo(T,T); // lets not over-complicate things for now
void foo(char);
void foo(int);
};
template<class U> struct S
{
template<int> struct size_map
{ typedef int type; };
// here is where we take the address of a possibly overloaded function
template<class T> void f(T,
typename size_map<sizeof(&U::foo)>::type* = 0);
void f(...);
};
int main()
{
S<X> s;
// should this cause a compiler error because 'auto T = &X::foo' …Run Code Online (Sandbox Code Playgroud) 考虑以下代码(它有点长,但希望你可以遵循):
class A
{
}
class B : A
{
}
class C
{
public virtual void Foo(B b)
{
Console.WriteLine("base.Foo(B)");
}
}
class D: C
{
public override void Foo(B b)
{
Console.WriteLine("Foo(B)");
}
public void Foo(A a)
{
Console.WriteLine("Foo(A)");
}
}
class Program
{
public static void Main()
{
B b = new B();
D d = new D ();
d.Foo(b);
}
}
Run Code Online (Sandbox Code Playgroud)
如果你认为这个程序的输出是"Foo(B)"那么你和我在同一条船上:完全错了!事实上,它输出"Foo(A)"
如果我从C类中删除虚方法,那么它按预期工作:"Foo(B)"是输出.
为什么编译器选择带有Awhen 的版本B是派生得更多的类?
clang和gcc在以下代码的行为上有所不同:
struct foo
{
foo(int);
};
struct waldo
{
template <typename T>
operator T();
};
int main()
{
waldo w;
foo f{w};
}
Run Code Online (Sandbox Code Playgroud)
clang接受此代码,并foo(int)调用构造函数.但是,gcc抱怨foo(int)构造函数和隐式生成的复制和移动构造函数之间存在歧义:
test.cpp: In function 'int main()':
test.cpp:15:12: error: call of overloaded 'foo(<brace-enclosed initializer list>)' is ambiguous
foo f{w};
^
test.cpp:15:12: note: candidates are:
test.cpp:3:5: note: foo::foo(int)
foo(int);
^
test.cpp:1:8: note: constexpr foo::foo(const foo&)
struct foo
^
test.cpp:1:8: note: constexpr foo::foo(foo&&)
Run Code Online (Sandbox Code Playgroud)
谁是对的?
值得注意的foo f{w}是,如果更改为foo f(w)(注意从大括号到括号的更改),gcc和clang都会出错.这让我希望gcc对上面例子的行为(即给出错误)是正确的,否则初始化()和{}形式之间会出现奇怪的不一致.
编辑 …
我有这个代码
struct A { A(); A(A&); };
struct B { B(const A&); };
void f(A);
void f(B);
int main() {
f(A());
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,GCC和Clang失败了.例如,Clang说
Compilation finished with errors:
source.cpp:8:10: error: no matching constructor for initialization of 'A'
f(A());
^~~
source.cpp:1:21: note: candidate constructor not viable: expects an l-value for 1st argument
struct A { A(); A(A&); };
^
source.cpp:1:16: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
struct A { A(); A(A&); };
^
source.cpp:4:13: note: passing …Run Code Online (Sandbox Code Playgroud) 我遇到了一个非模糊的奇怪情况,但过载解析器并不这么认为.考虑:
public static class Program
{
delegate int IntDel();
delegate string StringDel();
delegate void ParamIntDel(int x);
delegate void ParamStringDel(string x);
static void Test(IntDel fun) { }
static void Test(StringDel fun) { }
static void ParamTest(ParamIntDel fun) { }
static void ParamTest(ParamStringDel fun) { }
static int X() { return 42; }
static void PX(int x) { }
public static void Main(string[] args)
{
ParamTest(PX); // OK
Test(X); // Ambiguos call!
}
}
Run Code Online (Sandbox Code Playgroud)
为什么如何ParamTest正确解决对重载的调用,但是Test重载是不明确的?
当C#编译器解释方法调用时,它必须使用(静态)参数类型来确定实际调用哪个重载.我希望能够以编程方式执行此操作.
如果我有一个方法(a string)的名称,声明它的类型(一个实例System.Type),以及一个参数类型列表,我希望能够调用标准库函数并获取一个MethodInfo表示C#方法的对象编译器会选择调用.
例如,如果我有
class MyClass {
public void myFunc(BaseClass bc) {};
public void myFunc(DerivedClass dc) {};
}
Run Code Online (Sandbox Code Playgroud)
然后,我想是这样的虚构功能GetOverloadedMethod上System.Type
MethodInfo methodToInvoke
= typeof(MyClass).GetOverloadedMethod("myFunc", new System.Type[] {typeof(BaseClass)});
Run Code Online (Sandbox Code Playgroud)
在这种情况下methodToInvoke应该是public void myFunc(BaseClass bc).
注:既不方法GetMethod和GetMethods将成为我的目的.他们都没有做任何重载决议.在GetMethod它的情况下它只返回完全匹配.如果你给它更多的派生参数,它将只返回任何东西.或者你可能有幸得到一个模糊性异常,它没有提供有用的信息.
c# reflection system.reflection overload-resolution system-codedom-compiler
#include <iostream>
template <class U, class T>
void foo(U&, T&)
{
std::cout << "first";
}
template <class T>
void foo(int&, const T&)
{
std::cout << "second";
}
int main()
{
int a;
double g = 2.;
foo(a, g); // prints "first"
return 0;
}
Run Code Online (Sandbox Code Playgroud)
要调用第二个foo重载,编译器只需执行一次模板类型推导,但对于第一次重载,它需要执行两次.你能解释为什么第一次超载被调用了吗?
这是一个更复杂的问题,如果参数是一个重载函数,重载决策是如何工作的?
下面的代码编译没有任何问题:
void foo() {}
void foo(int) {}
void foo(double) {}
void foo(int, double) {}
// Uncommenting below line break compilation
//template<class T> void foo(T) {}
template<class X, class Y> void bar(void (*f)(X, Y))
{
f(X(), Y());
}
int main()
{
bar(foo);
}
Run Code Online (Sandbox Code Playgroud)
模板参数推导似乎不是一项具有挑战性的任务 - 只有一个函数foo()接受两个参数.但是,取消注释模板重载foo()(仍然只有一个参数)会破坏编译,没有明显的原因.使用gcc 5.x/6.x和clang 3.9编译失败.
可以通过重载决策/模板参数推导的规则来解释它还是应该被认为是那些编译器中的缺陷?
考虑这个代码:
#include <vector>
#include <iostream>
enum class A
{
X, Y
};
struct Test
{
Test(const std::vector<double>&, const std::vector<int>& = {}, A = A::X)
{ std::cout << "vector overload" << std::endl; }
Test(const std::vector<double>&, int, A = A::X)
{ std::cout << "int overload" << std::endl; }
};
int main()
{
std::vector<double> v;
Test t1(v);
Test t2(v, {}, A::X);
}
Run Code Online (Sandbox Code Playgroud)
这打印:
vector overload
int overload
Run Code Online (Sandbox Code Playgroud)
为什么由于重载解析不明确而不会产生编译错误?如果删除第二个构造函数,我们会得到vector overload两次。如何/由什么指标是int一个明确更好地匹配了{}比std::vector<int>?
构造函数签名当然可以进一步修剪,但我只是被一段等效的代码所欺骗,并想确保这个问题没有丢失任何重要的东西。