lis*_*rus 15 c++ lambda overloading c++11
考虑这个代码示例:
#include <iostream>
#include <functional>
typedef std::function<void()> func1_t;
typedef std::function<void(int)> func2_t;
struct X
{
X (func1_t f)
{ }
X (func2_t f)
{ }
};
int main ( )
{
X x([](){ std::cout << "Hello, world!\n"; });
}
Run Code Online (Sandbox Code Playgroud)
我确信它不应该编译,因为编译器不应该能够选择两个构造函数中的一个.g ++ - 4.7.3显示了这种预期的行为:它表示重载构造函数的调用是不明确的.但是,g ++ - 4.8.2成功编译了它.
这个代码在C++ 11中是正确的还是这个版本的g ++的bug /功能?
dyp*_*dyp 11
在C++ 11中......
让我们看看构造函数模板的规范std::function(它采用任何Callable):[func.wrap.func.con]/7-10
Run Code Online (Sandbox Code Playgroud)template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);7 要求:
F应该是CopyConstructible.对于参数类型和返回类型,f应为Callable(20.10.11.2).复制构造函数和析构函数不应抛出异常.ArgTypesRA8 后置条件:
!*this如果满足以下任何条件:
f是一个NULL函数指针.f是NULL指向成员的指针.F是函数类模板的一个实例,和!f9否则,将
*this目标作为f初始化的副本std::move(f).[在这里留下一个注释]10 抛出:当
f函数指针或reference_wrapper<T>某些函数指针时,不应抛出异常T.否则,可能会抛出bad_alloc或F复制或移动构造函数抛出的任何异常.
现在,构造或尝试构造(用于重载解析)a(std::function<void(int)>从[](){}签名void(void))违反了std::function<void(int)>构造函数的要求.
[res.on.required]/1
违反函数的Requires:段中指定的前提条件会导致未定义的行为,除非函数的Throws: paragraph指定在违反前提条件时抛出异常.
所以,AFAIK,即使是重载分辨率的结果也是未定义的.因此,g ++/libstdc ++的两个版本都符合这一方面.
在C++ 14中,这已经改变,参见LWG 2132.现在,std::functionSFINAE拒绝不兼容的Callables需要转换构造函数模板(下一章更多关于SFINAE):
Run Code Online (Sandbox Code Playgroud)template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);7 要求:
F应该是CopyConstructible.8 备注:除非
fCallable(20.9.11.2)用于参数类型ArgTypes...和返回类型,否则这些构造函数不应参与重载决策R.[...]
"不应参与超载分辨率"对应于SFINAE的拒绝.实际效果是,如果你有一组重载函数foo,
void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
Run Code Online (Sandbox Code Playgroud)
和一个如表达式的调用表达式
foo([](std::string){}) // (C)
Run Code Online (Sandbox Code Playgroud)
然后foo明确地选择第二个重载:由于std::function<F>定义F为它与外部的接口,F定义传递哪个参数类型std::function.然后,必须使用这些参数(参数类型)调用包装的函数对象.如果double传入a std::function,则无法将其传递给采用a的函数std::string,因为没有转换double- > std::string.因此,对于第一个重载foo,参数[](std::string){}不被视为可调用std::function<void(double)>.构造函数模板被停用,因此有一个从没有可行的转换[](std::string){}来std::function<void(double)>.第一个重载从过载集中删除,用于解析调用(C),只留下第二个重载.
请注意,一直到上面的措辞略有变化,由于LWG 2420:有,如果返回类型的例外R的std::function<R(ArgTypes...)>是void,那么任何返回类型被接受(和丢弃)在上述构造模板调用.例如,无论是[]() -> void {}和[]() -> bool {}是可赎回的std::function<void()>.因此,以下情况会产生歧义:
void foo(std::function<void()>);
void foo(std::function<bool()>);
foo([]() -> bool {}); // ambiguous
Run Code Online (Sandbox Code Playgroud)
重载决策规则不会尝试在不同的用户定义的转换之间进行排名,因此两个重载foo都是可行的(首先)并且两者都不是更好.
请注意,当SFINAE检查失败时,程序不会格式错误,但该功能不适用于重载分辨率.例如:
#include <type_traits>
#include <iostream>
template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{ std::cout << "foo 1\n"; }
template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{ std::cout << "foo 2\n"; }
int main()
{
foo(42);
foo(42.);
}
Run Code Online (Sandbox Code Playgroud)
类似地,通过在转换构造函数上使用SFINAE,可以使转换不可行:
#include <type_traits>
#include <iostream>
struct foo
{
template<class T, class =
typename std::enable_if< std::is_integral<T>::value >::type >
foo(T)
{ std::cout << "foo(T)\n"; }
};
struct bar
{
template<class T, class =
typename std::enable_if< not std::is_integral<T>::value >::type >
bar(T)
{ std::cout << "bar(T)\n"; }
};
struct kitty
{
kitty(foo) {}
kitty(bar) {}
};
int main()
{
kitty cat(42);
kitty tac(42.);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2831 次 |
| 最近记录: |