- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我想知道下面的代码是否有效。
我的初衷是,我喜欢一个基类,它可以将对某个成员的调用分派(dispatch)给派生类成员(如果派生类成员存在),或者如果派生类没有该成员则退回到默认行为。另一个用途是这个基类可以自己使用,Derived
模板参数成为一个实现策略。无论如何,以下 MWE 编译并正确运行 clang++
, 英特尔, icpc
和MSVS。但是它失败了 g++
(从 4.4 到 4.6,我接触过的任何版本)在问题末尾有错误消息。
如果我更改 call
在点 (1)、(2)、(3) 到 call_dispatch
(这是我最初做的那种事情),g++
不再提示了。我认为让调度函数和调用者同名不是一个好习惯。我只是很好奇它是否会起作用,并且好奇地尝试了一下(我不知道这个想法是怎么来的)。我的理由是,在品脱 (1) 时,call
是用一个参数调用的,所以重载决议将不匹配它的调用者,即零参数一。它不会匹配 SFINAE
一个在第 (2) 点,因为 D2
没有成员,那么它应该匹配第(3)点的成员。就像 (1)-(3) 被命名为 call_dispatch
的情况一样.
但是g++
不同意我和其他编译器。那么,这是否是 g++
的错误实现?还是代码本身无效?除了错误信息真的很困惑。 void (B<D2>::*)()
在哪里和 &B<D2>::call
来自?他调用的int成员指针被定义为D2
的成员。
#include <iostream>
#include <functional>
template <typename Derived>
class B
{
public :
void call ()
{
call<Derived>(0); //----------------------------------------- (1)
}
private :
template <typename D, void (D::*)()> class SFINAE {};
template <typename D>
void call (SFINAE<D, &D::call> *) //---------------------------- (2)
{
static_cast<Derived *>(this)->call();
}
template <typename D>
void call (...) //--------------------------------------------- (3)
{
std::cout << "Call B" << std::endl;
}
};
class D1 : public B<D1>
{
public :
void call ()
{
std::cout << "Call D1" << std::endl;
}
};
class D2 : public B<D2> {};
int main ()
{
D1 d1;
D2 d2;
d1.call();
d2.call();
return 0;
}
错误:
foo.cpp: In member function ‘void B<Derived>::call() [with Derived = D2]’:
foo.cpp:48:13: instantiated from here
foo.cpp:11:9: error: ‘&B<D2>::call’ is not a valid template argument for type ‘void (D2::*)()’ because it is of type ‘void (B<D2>::*)()’
foo.cpp:11:9: note: standard conversions are not allowed in this context
编辑
虽然我还没有完全理解上面的代码哪里出了问题。但我认为还有另一种方法,无需专门构造 SFINAE 类,但可以实现相同的效果。
#include <iostream>
template <typename Derived>
class B
{
public :
void call ()
{
call_dispatch(&Derived::call);
}
template <typename C>
void call_dispatch (void (C::*) ())
{
static_cast<Derived *>(this)->call();
}
void call_dispatch (void (B<Derived>::*) ())
{
std::cout << "Call B" << std::endl;
}
private :
};
class D1 : public B<D1>
{
public :
void call ()
{
std::cout << "Call D1" << std::endl;
}
};
class D2 : public B<D2> {};
int main ()
{
D1 d1;
D2 d2;
d1.call();
d2.call();
return 0;
}
基本上,因为 D1
和 D2
两者都来自 B
, 所以表达式 &Derived::call
总会解决的。在 D1
它解决了&D1::call
, 然后使用模板版本成员。在 D2
, 它没有自己的 call
, 所以 &D2::call
解析为 &B::call
, 感谢
@DavidRodríguez-dribeas,他现在指出 &D2::call
类型为 B::call
,因此模板和非模板成员同样匹配,但首选非模板。所以使用默认调用。
能帮我看看这个新代码有没有缺陷?
最佳答案
Where does the void (B::*)() and &B::call come from?
指向成员的指针的类型不是您获得该指针的类型,而是定义成员的类型。
struct base { int x; };
struct derived : base {};
int main() {
std::cout << std::is_same< decltype(&derived::x), int (base::*) >::value << std::endl;
}
上面的程序打印1
.在您的情况下,当您使用 &D::base
, 编译器找到 B<D2>::call
作为基本模板的成员,这是表达式的结果:void (B<D2>::*)()
.
关于C++ SFINAE with CRTP, G++ 编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11042313/
C++11 专家的几个问题。 我正在与 SFINAE 打交道,我遇到了一个奇怪的情况,其中 g++ (4.9.2) 和 clang++ (3.5.0) 的行为不同。 我准备了以下示例代码。很抱歉,我无
这些天我正在试验 SFINAE,有些事情让我很困惑。为什么 my_type_a不能在 my_function 中推导出来的实例化? class my_type_a {}; template clas
我正在尝试复制(我猜)典型的 SFINAE 示例来判断一个类型是否具有特定方法。我的代码基本上是 the one found in the accepted answer of a previous
出于学术原因,我想实现一个示例,如果非类型模板参数满足给定条件,则选择一个模板。例如,我想要一个只为奇数定义的函数。 可以这样做: template struct is_odd: public st
有没有办法检查两个可变参数包的串联是否与第三个可变参数包相同。 template struct ClassOne { } template struct ClassTwo { } template s
为什么以下代码无法编译?为具有特定成员的类型启用模板的最简洁的解决方案是什么?如果模板变量被直接用于初始化它的表达式替换,它也会编译。 #include template constexpr boo
代码: #include using std::nullptr_t; template using nullptr_vt = nullptr_t; struct not_addable{}; tem
我有一个模板类,我想有两个复制 ctor。一个用于平凡类型,另一个用于非平凡类型。 以下代码有效(使用一个拷贝 ctor): template struct MyStruct { MySt
我一直在尝试定义一个辅助类来帮助我使用模板方法,在该方法中我希望为复杂类型和实际类型提供通用实现。 到目前为止,这是我的尝试: #include #include template struct is
这个问题已经有答案了: Officially, what is typename for? [duplicate] (8 个回答) 已关闭 3 年前。 我正在研究现代 C++ 中的 SFINAE,我看
我有一个 std::variants 包含具有不同接口(interface)的对象。目标是如果变体中的对象具有某些方法,则调用它。 我正在尝试制作几个模板装饰器,并寻找一种方法来使用更少的样板和没有宏
我有一个代码,它接受一个函数并根据函数签名执行它,如下所示: template struct Value { int value[Num]; }; struct Executor { t
我一直在尝试定义一个辅助类来帮助我使用模板方法,在该方法中我希望为复杂类型和实际类型提供通用实现。 到目前为止,这是我的尝试: #include #include template struct is
在此视频中https://youtu.be/Vkck4EU2lOU?t=582 “标签调度”和SFINAE作为替代方案出现,允许实现所需模板功能的选择。 正确吗? “标签发送”不是使用SFINAE吗?
我认为下面的代码会编译,因为冲突的重载是 SFINAEd 了。但是编译器(GCC)说:void Foo::bar(Xd) const' cannot be overloaded .有没有简单的方法来修
#define BINDINGTEMPLATE template, int> || std::is_same_v, std::string> || std::is_same_v, char>>> 这样
SFINAE 是否在概念论证中起作用? (也许这里不叫 SFINAE)。例子: template requires std::invocable && // , void>)
在他的演讲现代模板元编程:纲要第 I 部分中,Walter Brown 以他的方式讨论了 enable_if 与 SFINAE 的交互。 在大约 47:40 的谈话中,他被问到一个问题,我无法完全匹配
我正在尝试对同一函数进行两次重载,称为某物。这个函数应该以另一个函数作为参数,并且它应该根据这个另一个函数的返回类型进行重载。到目前为止我有这个: #include #include using
以下代码特化了 f() 的两个版本。第一个检测一个 vector 并返回一个迭代器。第二个接受所有其他类型并返回一个拷贝。 这无法在 VC 2010 上编译,GetIter2 中有一个错误,即 Get
我是一名优秀的程序员,十分优秀!