这种情况会发生什么:
struct A {
void f();
};
struct B : virtual A {
using A::f;
};
struct C : virtual A {
using A::f;
};
struct D : B, C {
void g() {
f();
}
};
Run Code Online (Sandbox Code Playgroud)
感兴趣的是 f().显然,f根据10.2FDIS 的查找成功并找到A::f.但是,哪些候选人会考虑重载决议?规范说13.3.1p4:
对于由using声明引入到派生类中的非转换函数,该函数被认为是派生类的成员,用于定义隐式对象参数的类型.
这样做的目的是使单个类,如果这样一类同时包含自己的成员函数和using声明带来的基类函数的名称为范围,即重载解析过程中的所有功能,考生在他们的隐含对象是相同类型参数.但这对于上面的例子意味着什么呢?候选人是否会如下?
void F1(B&)
void F2(C&)
// call arguments: (lvalue D)
Run Code Online (Sandbox Code Playgroud)
这似乎是错误的,因为根据我们在查找结果集中只有一个声明10.2p7.我们该如何解读?
我正在通过解释器进行一些动态的代码调用,而我正在进入JLS第15.12节中讨论的方法解决的粘性丑陋区域.
选择方法的"简单"方法是,当您知道所有参数的确切类型时,您可以使用该方法Class.getDeclaredMethod(String name, Class[] parameterTypes).也许您必须检查方法可访问性和类的超类/超级接口.
但这并不包括以下任何一种情况,所以它有点无用:
int但调用者传入的数字是a int或a double)(见下面的前三个快速示例)
所以现在我必须编写自己的方法解析库...
有没有任何着名的框架库来协助这个?
package com.example.test.reflect;
import java.lang.reflect.Method;
public class MethodResolutionTest {
public void compute(int i) { /* implementation... */ }
public void compute(Long l) { /* implementation... */ }
public void compute(Object obj) { /* implementation... */ }
public void compute(String... strings) { /* implementation... */ }
public static void main(String[] args) {
Class<?> cl = …Run Code Online (Sandbox Code Playgroud) 如何检查过载分辨率设置?
我在多个呼叫站点中使用了4个竞争功能.在一个调用站点中,我期望调用一个函数,但编译器会选择另一个函数.我不知道为什么/它不是微不足道的.要了解正在发生的事情,我正在使用enable_if/disable_if打开/关闭功能,但这实在是很慢/乏味/烦人.
所以我希望编译器告诉我"为什么?".也就是说,对于这个单一呼叫站点:
不需要有关访问控制的信息.
基本上我希望用一个#pragma或类似的(__builtin...)标记呼叫站点.但libclang也是一种选择.
我可以访问tip-of-trunk clang和gcc,但如果需要可以安装其他编译器/工具.
我的问题是由Eric Lippert的博客文章推动的.请考虑以下代码:
using System;
class Program {
class A {}
class B {}
static void M(A x, B y) { Console.WriteLine("M(A, B)"); }
static void Call(Action<A> f) { f(new A()); }
static void Call(Action<B> f) { f(new B()); }
static void Main() { Call(x => Call(y => M(x, y))); }
}
Run Code Online (Sandbox Code Playgroud)
这种成功并打印编译M(A, B),因为编译器计算出该类型的x和y在lambda表达式应该是A和B分别.现在,添加一个重载Program.M:
using System;
class Program {
class A {}
class B {}
static void …Run Code Online (Sandbox Code Playgroud) 大多数IO流操纵器都是具有以下签名的常规函数:
std::ios_base& func( std::ios_base& str );
Run Code Online (Sandbox Code Playgroud)
但是,一些操纵器(包括最常用的操纵器 - std::endl和std::flush)是以下形式的模板:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& func(std::basic_ostream<CharT, Traits>& os);
Run Code Online (Sandbox Code Playgroud)
然后,如果std::cout << std::endl;以下示例失败,如何编译成功:
$ cat main.cpp
#include <iostream>
int main()
{
auto myendl = std::endl;
std::cout << myendl;
}
$ g++ -std=c++11 main.cpp -o main
main.cpp: In function ‘int main()’:
main.cpp:5:24: error: unable to deduce ‘auto’ from ‘std::endl’
auto myendl = std::endl;
^
Run Code Online (Sandbox Code Playgroud)
很明显,上下文(in std::cout << std::endl;)有助于编译器消除对引用的歧义std::endl.但是,管理该程序的规则是什么?对于重载分辨率来说,这似乎是一个真正的挑战,它必须同时回答两个问题:
std::endl<CharT, Traits>() …一个 C++ 函数可以有多个参数包。虽然看起来不太实用,但了解它们的语言规则仍然很有趣。
例如,如果有两个重载:
constexpr int f(auto...) { return 1; }
constexpr int f(auto..., auto...) { return 2; }
Run Code Online (Sandbox Code Playgroud)
f不带参数的调用f()在 MSVC 中选择版本 1,在 Clang 中选择版本 2,ambiguous overloaded call在 GCC 中选择版本 2。
如果f使用参数调用f(1),则 MSVC 和 GCC 都选择版本 1,而 Clang 仍选择版本 2。
演示: https: //gcc.godbolt.org/z/PWr6h1dn1
这里是哪个编译器?
有一个类似的问题带有两个参数包的函数模板重载解析,但是
c++ language-lawyer overload-resolution c++20 parameter-pack
让我们有两个成员相同的签名,但一个是静态的而另一个是 - 不是:
class Foo
{
public void Test() { Console.WriteLine("instance"); }
public static void Test() { Console.WriteLine("static"); }
}
Run Code Online (Sandbox Code Playgroud)
但是这样的代码生成会带来编译错误:
类型'Foo'已经定义了一个名为'Test'的成员,它具有相同的参数类型
但为什么?
让我们成功编译,然后:
Foo.Test() 应输出"静态"
new Foo().Test();应该输出"实例"
无法调用静态成员而不是实例1,因为在这种情况下会出现另一个更合理的编译器错误:
无法使用实例引用访问成员'Foo.Test()'; 用类型名称来限定它
这是我不理解的代码:
class Base
{
public:
Base(){}
Base operator=(Base ob2)
{
std::cout << "Using Base operator=() " << '\n';
return *this;
}
};
class Derived : public Base
{
public:
Derived(){}
Derived operator=(Base ob2)
{
std::cout << "Using Derived operator=() " << '\n';
return *this;
}
};
int main()
{
Derived derived1, derived2;
Base base1;
derived1 = derived2; // Uses base operator=()
derived1 = base1; // Uses derived operator=()
return 0;
}
Run Code Online (Sandbox Code Playgroud)
确定的第一个赋值使用Base类的运算符,第二个赋值使用Derived类的运算符的语言规则是什么?
是的,我知道通常不会像这样声明赋值运算符.这就是为什么我称它为accademical.
在某些条件下,我想要SFINAE远离类模板的复制构造函数和复制赋值运算符.但是如果我这样做,则会生成默认的复制构造函数和默认赋值运算符.SFINAE基于我作为类模板参数传递的标签完成.问题是,SFINAE仅适用于模板,复制构造函数/赋值运算符不能作为模板.是否存在变通方法?
考虑以下:
struct X {
template <class T> operator T(); // #1
template <class T> operator T&(); // #2
};
int a = X{}; // error: ambiguous
int& b = X{}; // calls #2
int const& c = X{}; // calls #2
Run Code Online (Sandbox Code Playgroud)
这种情况b很简单,#2是唯一可行的候选人.表示初始化的#2首选规则是什么,但两者对于初始化是不明确的?#1int const&int