考虑以下代码:
#include <cstdio>
struct S {
S(int) {}
};
void f(const S&) { std::puts("const S&"); }
void f(S&&) { std::puts("S&&"); }
int main() {
f(42);
}
Run Code Online (Sandbox Code Playgroud)
很明显,GCC 和 Clang 都同意应该调用重载S&&,但我对标准的解读是,实际上没有规则要求这种情况!
当然,您会说调用f必须初始化一个临时S对象,并且重载解析更喜欢将右值引用绑定到右值而不是绑定左值引用。然而,这就是标准在 [over.ics.rank]/(3.2) 中实际所说的内容:
标准转换序列
S1是比标准转换序列更好的转换序列,S2如果...
S1和S2是引用绑定 (11.6.3),两者都不引用未使用ref-qualifier声明的非静态成员函数的隐式对象参数,并且S1将右值引用绑定到右值并S2绑定左值引用...
因此,有关左值和右值引用绑定的决胜局仅适用于标准转换序列。但是需要用户定义的f转换序列才能调用给定int参数的任一重载。用户自定义转换序列的排序规则为(3.3),
如果用户定义的转换序列
U1包含相同的用户定义的转换函数或构造函数,或者它们在聚合初始化中初始化相同的类,并且在这两种情况下,第二个标准转换序列是,则用户定义的转换序列是U2比另一个用户定义的转换序列更好的转换序列U1优于 的第二个标准转换序列U2。
显然,两个转换序列(toconst …
以下代码与clang(x86_64-pc-linux-gnu 上的版本 5.0.0-3~16.04.1)和gcc (9.2.0) 的行为不同。
void operator delete(void* ptr) noexcept {
std::cout << "overloaded delete" << std::endl;
free(ptr);
}
int main() {
int* pint = new int;
delete pint; // clang doesn't go through overloaded operator delete, gcc does
}
Run Code Online (Sandbox Code Playgroud)
gcc通过重载,operator delete而clang避免它更喜欢global delete.
(当添加重载时,operator new两个编译器都同意并通过两个重载版本)。
Java 11(可能无关紧要):
public static String toString(Object obj) {
return ReflectionToStringBuilder.toString(obj, ToStringStyle.SHORT_PREFIX_STYLE);
}
public static String toString(Collection<Object> collection) {
return collection.stream()
.map(SaLogUtils::toString)
.collect(Collectors.joining(", ", "[", "]"));
}
public static void main(String[] args) {
List<Integer> list = List.of(Integer.valueOf(1));
System.out.println(SaLogUtils.toString(list));
System.out.println(SaLogUtils.toString(List.of(Integer.valueOf(1))));
}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的输出:
// from toString(Object)
ImmutableCollections.List12[e0=1,e1=<null>]
// from toString(Collection<Object>)
[Integer[value=1]]
Run Code Online (Sandbox Code Playgroud)
为什么Java静态选择不同的方法?
java generics overload-resolution static-polymorphism java-11
我有以下代码片段:
void eval(void*) { std::cout << "hello world\n"; }
void eval(...) { }
int main(int argc, char *argv[])
{
std::cout << "0: "; eval(0);
std::cout << "1: "; eval(1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这给出了输出:
0: hello world
1:
Run Code Online (Sandbox Code Playgroud)
我的问题是:为什么重载解析选择的void*版本eval而不是...for的版本0,而不是 for 1?似乎在这两种情况下,它都可以推断出参数是一个 int 并采用可变参数版本。
我基本上想要 string/FormattableString 的两个单独的重载(背景是我想推动人们使用字符串常量来记录日志消息并通过结构化日志而不是日志消息传递参数以简化分析。因此 FormattableString 日志记录方法将被废弃)。
现在由于编译器的工作方式,您不能直接重载方法,因为 FormattableString 在传递之前会转化为字符串。但有效的是有一个定义隐式重载的包装结构:
public struct StringIfNotFormattableStringAdapter
{
public string StringValue { get; }
private StringIfNotFormattableStringAdapter(string s)
{
StringValue = s;
}
public static implicit operator StringIfNotFormattableStringAdapter(string s)
{
return new StringIfNotFormattableStringAdapter(s);
}
public static implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
{
throw new InvalidOperationException("This only exists to allow correct overload resolution. " +
"This should never be called since the FormattableString overload should be preferred to this.");
}
}
public static class Test
{
public static void …Run Code Online (Sandbox Code Playgroud) 函数“Process”正在接受可变数量的变量类型参数。为了处理不同的情况,我已经成功地像这样重载了它:
// general case
template <typename ...Types>
void Process( const Types&... items )
// single T
template <typename T>
void Process( const T& t )
// one or more of type NVP<>
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps )
Run Code Online (Sandbox Code Playgroud)
我想要做的 - 但不能 - 如下:对于具有任意数量的类型的前导参数ATT<>后跟任意数量的情况,我需要重载NVP<>:
// any number of leading Types ATT<> followed by any number of NVP<>
template <typename ...ATypes, typename ...BTypes>
void Process( const ATT<ATypes>&... …Run Code Online (Sandbox Code Playgroud) c++ variadic-functions overload-resolution variadic-templates c++17
对于通用自由函数,我可以使用重载,从本质上将函数专门化为函数类型,如下所示:
func foo<T>(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: ((P) -> Void).Type) { print("T is a function with one parameter") }
let f: (String) -> Void = { print($0) }
foo(type(of: f)) // prints "T is a function with one parameter"
Run Code Online (Sandbox Code Playgroud)
注意第二个版本foo()是不受协议约束的,主要是因为据我所知,我们不能让函数类型符合协议(我们不能扩展非名义类型)。我可以创建一个OneParamFunction协议,并且可以在一个受约束的 中使用它foo(),但是我不能让所有的单参数函数类型都符合那个协议。
但是上述重载在没有协议约束的情况下工作。
对于泛型类的实例方法,这样的事情可能吗?
对我来说,这种语法似乎是最自然的,但它不受支持:
class Generic1<T> { init(_ t: T.Type) {} }
extension Generic1 { func foo() { print("T is unknown") } }
extension Generic1<P>
where …Run Code Online (Sandbox Code Playgroud) 考虑这段代码:
fun main() {
class A<T> {
fun m(t: T): Unit {
print("T")
}
fun m(t: List<T>): Unit {
print("List<T>")
}
}
val a: A<Any> = A()
val l: List<Any> = listOf()
a.m(l)
}
Run Code Online (Sandbox Code Playgroud)
该a.m(l)调用似乎不明确,并出现以下错误:
重载解析歧义: public final fun m(t: Any): main.A 中定义的单位 public final fun m(t: List<Any>): main.A 中定义的单位
我的直觉告诉我,m(t: List<T>)重载应该更具体,但我的直觉已经错误了一次,当时我在 Java 中遇到过类似的情况。
我可以这样称呼“错误”的重载:
a.m(l as Any)
Run Code Online (Sandbox Code Playgroud)
但是我怎样才能明确地调用所需的m(t: List<T>)重载呢?铸造a.m(l as List<Any>)不起作用。
编译失败的例子:
class A{
public:
int f(int a) {return a;}
};
class B: public A {
public:
int f(int a, int b) {return a + b;}
};
int calculation(int num) {
B b;
return b.f(num);
}
Run Code Online (Sandbox Code Playgroud)
在调用站点b.f(num),gcc给出以下错误消息:
错误:没有匹配的函数可供调用
B::f(int&)
基类有一个可行的候选者,但由于某种原因编译器不会考虑它。
如果我将调用重写为b.A::f(num),那么它就可以正常工作。我不明白为什么这A::是必要的。为什么 的A::f重载解析逻辑不将其视为可行的候选者b.f?
在下面的代码中,struct A有两个构造函数:A(int)和A(std::initializer_list<char>)。然后使用以下命令创建该结构的对象A({0}):
#include <initializer_list>
struct A {
int a;
constexpr A(int) { a = 1; }
constexpr A(std::initializer_list<char>) { a = 2; }
};
static_assert( A({0}).a == 2 );
Run Code Online (Sandbox Code Playgroud)
事实证明,GCC 和 Clang 更喜欢这里的构造函数initializer_list(尽管int必须在 中转换参数char)并且整个程序被接受,但 MSVC 选择A(int)构造函数,导致static_assert. 演示: https: //gcc.godbolt.org/z/qx7W417Mv
这里是哪个编译器?