在阅读了如何使这些std :: function参数明确无误后,我完全糊涂了?到目前为止,我认为我理解函数模板的部分排序是什么,但在阅读了这个问题后,我写了三个例子来检查编译器的行为,并且收到的结果很难让我理解.
template <class T>
void foo(T) {}
template <class T>
void foo(T&) {}
int main()
{
int i;
foo<int>(i); // error: call is ambiguous!
}
Run Code Online (Sandbox Code Playgroud)
问题:这两个功能都是可行的,这是显而易见的,但不是那个T&比专业更专业的功能T?相反,编译器会引发模糊的调用错误.
#include <iostream>
template <class T>
struct X {};
template <>
struct X<int>
{
X() {}
X(X<int&> const&) {} // X<int> is constructible from X<int&>
// note: this is not a copy constructor!
};
template <>
struct X<int&> …Run Code Online (Sandbox Code Playgroud) c++ templates partial-ordering language-lawyer overload-resolution
在以下代码中,struct A有两个隐式转换运算符到char和int,并且将结构体的实例与整数常量进行比较2:
struct A {
constexpr operator char() { return 1; }
constexpr operator int() { return 2; }
};
static_assert( A{} == 2 );
Run Code Online (Sandbox Code Playgroud)
代码在 GCC 和 MSVC 中顺利通过,但 Clang 抱怨:
<source>:5:20: error: use of overloaded operator '==' is ambiguous (with operand types 'A' and 'int')
static_assert( A{} == 2 );
~~~ ^ ~
<source>:5:20: note: because of ambiguity in conversion of 'A' to 'float'
<source>:2:15: note: candidate function
constexpr operator …Run Code Online (Sandbox Code Playgroud) 我很确定这一定已经在这里了,但是我没有找到很多关于如何解决这类问题的信息(没有在电话上强制转换):
给定两个重载,我希望一个带有文字0的函数的调用总是调用unsigned int版本:
void func( unsigned int ) {
cout << "unsigned int" << endl;
}
void func( void * ) {
cout << "void *" << endl;
}
func( 0 ); // error: ambiguous call
Run Code Online (Sandbox Code Playgroud)
我明白为什么会这样,但我不想一直写func(0u)甚至func(static_cast(0)).所以我的问题是:
1)是否有推荐的方法来执行此操作?
2)以下方式进行操作是否有任何问题,这是什么原因?
void func( unsigned int ) {
cout << "unsigned int" << endl;
}
template <typename T>
void func( T * ) {
static_assert( std::is_same<T, void>::value, "only void pointers allowed" );
cout << "void *" << endl;
}
func( 0 ); // …Run Code Online (Sandbox Code Playgroud) void foo(int)
{
}
class X
{
void foo()
{
}
void bar()
{
foo(42);
// error: no matching function for call to 'X::foo(int)'
// note: candidate is:
// note: void X::foo()
// note: candidate expects 0 arguments, 1 provided
}
};
Run Code Online (Sandbox Code Playgroud)
为什么C++无法调用自由函数(这是唯一具有正确签名的函数)?
我发现C++解析运算符重载的奇怪行为,我无法解释自己.指向描述它的某个资源的指针就像答案一样好.
我有2个翻译单位.在一个(称为util.cpp/h)中,我声明并定义了两个运算符(我省略了readabilty的实际实现,无论如何都会发生problam):
// util.h
#ifndef GUARD_UTIL
#define GUARD_UTIL
#include <iostream>
std::istream& operator>>(std::istream& is, const char* str);
std::istream& operator>>(std::istream& is, char* str);
#endif
Run Code Online (Sandbox Code Playgroud)
和:
//util.cpp
#include "util.h"
#include <iostream>
std::istream& operator>>(std::istream& is, const char* str) {
return is;
}
std::istream& operator>>(std::istream& is, char* str) {
return is;
}
Run Code Online (Sandbox Code Playgroud)
如果在全局命名空间中,这些运算符是因为它们在std类型和内置类型上运行,并且应该可以在任何地方使用.它们只能从全局命名空间(例如来自main())或明确地告诉编译器它们在全局命名空间中工作(参见代码示例).
在另一个翻译单元(称为test.cpp/h)中,我在命名空间中使用这些运算符.这有效,直到我将类似的运算符放入此命名空间.一旦添加此运算符,编译器(例如gcc或clang)就不能再找到可行的运算符>>.
// test.h
#ifndef GUARD_TEST
#define GUARD_TEST
#include <iostream>
namespace Namespace {
class SomeClass {
public:
void test(std::istream& is);
};
// without the following line everything compiles just fine
std::istream& operator>>(std::istream& is, …Run Code Online (Sandbox Code Playgroud) 为什么第一个函数call(cm(car);)绑定到第一个函数?
我知道第二个调用绑定到第二个函数,因为它是非模板,尽管两者都是完美的匹配.
如果第一个函数被定义为具有固定数组长度的非模板,则:
void cm(const char (&h)[8]) {cout << "const char (&)[8]" << endl;}
Run Code Online (Sandbox Code Playgroud)
而不是它再次被选中在第二个(第二个调用将是不明确的那种方式).
码:
template<size_t N> void cm(const char (&h)[N])
{std::cout << " const (&)[N] " << endl;}
void cm(const char * h)
{cout << " const char * " << endl;}
int main()
{
char car[] = "errqweq";
const char ccar[] = "errqweq";
cm(car);
cm(ccar);
}
Run Code Online (Sandbox Code Playgroud)
输出:
const (&)[N]
const char *
Run Code Online (Sandbox Code Playgroud) 以下代码无法编译:
#include <iostream>
#include <utility>
struct Foo
{
Foo() { std::cout << "Foo()" << std::endl; }
Foo(int) { std::cout << "Foo(int)" << std::endl; }
};
template <typename T>
struct Bar
{
Foo foo;
Bar(const Bar&) { std::cout << "Bar(const Bar&)" << std::endl; }
template <typename... Args>
Bar(Args&&... args) : foo(std::forward<Args>(args)...)
{
std::cout << "Bar(Args&&... args)" << std::endl;
}
};
int main()
{
Bar<Foo> bar1{};
Bar<Foo> bar2{bar1};
}
Run Code Online (Sandbox Code Playgroud)
编译器错误告诉我编译器试图使用variadic模板构造函数而不是复制构造函数:
prog.cpp: In instantiation of 'Bar<T>::Bar(Args&& ...) [with Args = {Bar<Foo>&}; T = …Run Code Online (Sandbox Code Playgroud) 我有一个简单的工厂模式,其中的实现是通过重载决定来确定的.问题是Kotlin编译器抱怨内联lambda的"Overload resolution ambiguity ..".
class Foo(){
companion object Factory {
fun create(x: Int, f: (Int) -> Double) = 2.0
fun create(x: Int, f: (Int) -> Int) = 1
}
}
fun main(args:Array<String>){
val a = Foo.create(1,::fromDouble) //OK
val b = Foo.create(1,::fromInt) //OK
val ambiguous = Foo.create(1){i -> 1.0} //Overload resolution ambiguity?
}
fun fromDouble(int:Int) = 1.0
fun fromInt(int:Int) = 1
Run Code Online (Sandbox Code Playgroud)
Kotlin编译器如何解决重载解析,为什么内联lambda被认为是不明确的?
以下代码成功编译了大多数现代C++ 11兼容编译器(GCC> = 5.x,Clang,ICC,MSVC).
#include <string>
struct A
{
explicit A(const char *) {}
A(std::string) {}
};
struct B
{
B(A) {}
B(B &) = delete;
};
int main( void )
{
B b1({{{"test"}}});
}
Run Code Online (Sandbox Code Playgroud)
但是为什么它首先编译,以及列出的编译器如何解释该代码?
为什么MSVC能够在没有的情况下编译它B(B &) = delete;,但其他3个编译器都需要它?
为什么在删除复制构造函数的不同签名时,除了MSVC之外的所有编译器都会失败,例如B(const B &) = delete;?
编译器甚至都选择相同的构造函数吗?
为什么Clang会发出以下警告?
17 : <source>:17:16: warning: braces around scalar initializer [-Wbraced-scalar-init]
B b1({{{"test"}}});
Run Code Online (Sandbox Code Playgroud) 由于重载解析不明确,此代码示例无法编译
void g(char (&t)[4]) {}
void g(char *t) {}
int main()
{
char a[] = "123";
g(a);
}
Run Code Online (Sandbox Code Playgroud)
仔细阅读重载解析规则可以清楚为什么失败。这里没有问题。
如果我们正式将其改造为模板版本
template <typename T> void g(T (&t)[4]) {}
template <typename T> void g(T *t) {}
int main()
{
char a[] = "123";
g(a);
}
Run Code Online (Sandbox Code Playgroud)
它将继续“按预期”运行,并因同样性质的模糊性而失败。到目前为止,一切都很好。
然而,下面的版本编译没有任何问题并选择了第二个重载
template <typename T> void g(T &t) {}
template <typename T> void g(T *t) {}
int main()
{
char a[] = "123";
g(a);
}
Run Code Online (Sandbox Code Playgroud)
如果我们注释掉第二个重载,第一个重载将成功地与推导为T一起使用char [4],即模板参数推导按第一个版本的预期工作,有效地使其相当于void g(char (&t)[4])。因此,乍一看,第三个示例的行为应该与前两个示例相同。
然而,它可以编译。在第三种情况下,什么[模板]重载解析规则可以挽救局面并指示编译器选择第二个重载?为什么它更喜欢数组到指针的转换而不是直接引用绑定?
PS …
c++ ×9
templates ×5
c++11 ×2
function ×2
arrays ×1
kotlin ×1
lambda ×1
operators ×1
overloading ×1
visibility ×1