ADL 相关的 GCC 4.7.2 问题与表达式 SFINAE
ADL related GCC 4.7.2 issue with expression SFINAE
取下面代码,其特点是
- 特定行为依赖日常生活能力(
volume
)
- 对 return 类型使用 decltype 并依靠 SFINAE 丢弃额外的重载
namespace Nature {
struct Plant {};
double volume(Plant){ return 3.14; }
}
namespace Industrial {
struct Plant {};
double volume(Plant) { return 100; }
}
namespace SoundEffects {
// A workaround for GCC, but why?
////template<class T> void volume();
template<class aSound>
auto mix(aSound& s) -> decltype(volume(s)*0.1)
{
return volume(s)*.1;
}
struct Samples {
Nature::Plant np;
Industrial::Plant ip;
};
inline double mix(const Samples& s) {
return mix(s.np) + mix(s.ip);
}
}
int main()
{
SoundEffects::Samples s;
assert( mix(s) == 100*.1 + 3.14*.1 );
}
显示的代码(没有 template<class T> void volume()
行),VS 2012 和 clang 3.5 编译成功,并且 运行time 符合预期。但是,GCC 4.7.2 说:
template-function-overload.cpp: In substitution of 'template<class aSound> decltype ((volume(s) * 1.0000000000000001e-1)) SoundEffects::mix(aSound&) [with aSound = SoundEffects::Samples]':
template-function-overload.cpp:46:4: required from here
template-function-overload.cpp:23:9: error: 'volume' was not declared in this scope
template-function-overload.cpp:23:9: note: suggested alternatives:
template-function-overload.cpp:9:11: note: 'Nature::volume'
template-function-overload.cpp:14:11: note: 'Industrial::volume'
有了额外的 template volume
行,所有三个都可以编译并且 运行 没问题。
所以,这里显然存在编译器缺陷。我的问题是,哪个编译器有缺陷?违反了哪个 C++ 标准?
这是一个错误,自 GCC 4.8. Here's a simplified version 出现相同错误的代码以来已修复:
template<class T>
auto buzz(T x) -> decltype(foo(x));
void buzz(int);
int main() {
buzz(5); // error: 'foo' was not declared in this scope
}
在mix(s)
中,SoundEffects::mix
的两个重载都通过ADL编译成候选重载集(SoundEffects
是SoundEffects::Sample
的关联命名空间)。评估函数模板重载的可行性。发生错误的原因是 volume(s)
无法通过纯非限定查找或 Sample
.
的 ADL 解析为合适的重载
这个 应该 通过的原因是当查找失败时应该发生替换失败(因为 volume(s)
是依赖的)并且模板应该被重载决议拒绝。这将使 mix(const Sample&)
成为 select 的唯一可行重载。存在硬错误的事实显然表明此 GCC 版本的 SFINAE 实现有问题。
取下面代码,其特点是
- 特定行为依赖日常生活能力(
volume
) - 对 return 类型使用 decltype 并依靠 SFINAE 丢弃额外的重载
namespace Nature {
struct Plant {};
double volume(Plant){ return 3.14; }
}
namespace Industrial {
struct Plant {};
double volume(Plant) { return 100; }
}
namespace SoundEffects {
// A workaround for GCC, but why?
////template<class T> void volume();
template<class aSound>
auto mix(aSound& s) -> decltype(volume(s)*0.1)
{
return volume(s)*.1;
}
struct Samples {
Nature::Plant np;
Industrial::Plant ip;
};
inline double mix(const Samples& s) {
return mix(s.np) + mix(s.ip);
}
}
int main()
{
SoundEffects::Samples s;
assert( mix(s) == 100*.1 + 3.14*.1 );
}
显示的代码(没有 template<class T> void volume()
行),VS 2012 和 clang 3.5 编译成功,并且 运行time 符合预期。但是,GCC 4.7.2 说:
template-function-overload.cpp: In substitution of 'template<class aSound> decltype ((volume(s) * 1.0000000000000001e-1)) SoundEffects::mix(aSound&) [with aSound = SoundEffects::Samples]':
template-function-overload.cpp:46:4: required from here
template-function-overload.cpp:23:9: error: 'volume' was not declared in this scope
template-function-overload.cpp:23:9: note: suggested alternatives:
template-function-overload.cpp:9:11: note: 'Nature::volume'
template-function-overload.cpp:14:11: note: 'Industrial::volume'
有了额外的 template volume
行,所有三个都可以编译并且 运行 没问题。
所以,这里显然存在编译器缺陷。我的问题是,哪个编译器有缺陷?违反了哪个 C++ 标准?
这是一个错误,自 GCC 4.8. Here's a simplified version 出现相同错误的代码以来已修复:
template<class T>
auto buzz(T x) -> decltype(foo(x));
void buzz(int);
int main() {
buzz(5); // error: 'foo' was not declared in this scope
}
在mix(s)
中,SoundEffects::mix
的两个重载都通过ADL编译成候选重载集(SoundEffects
是SoundEffects::Sample
的关联命名空间)。评估函数模板重载的可行性。发生错误的原因是 volume(s)
无法通过纯非限定查找或 Sample
.
这个 应该 通过的原因是当查找失败时应该发生替换失败(因为 volume(s)
是依赖的)并且模板应该被重载决议拒绝。这将使 mix(const Sample&)
成为 select 的唯一可行重载。存在硬错误的事实显然表明此 GCC 版本的 SFINAE 实现有问题。