为什么 SFINAE (enable_if) 在类定义内部起作用,但在外部不起作用

2024-05-02

过去几个小时我一直在努力解决这个非常奇怪的问题(在解决了 SFINAE 的 5-6 个其他问题之后,因为我是新手)。基本上在下面的代码中我想要f()适用于所有可能的模板实例化,但有g()仅当N == 2:

#include <type_traits>
#include <iostream>

template<typename T, int N>
class A
{
public:
    void f(void);
    void g(void);
};

template<typename T, int N>
inline void A<T, N>::f()
{
    std::cout << "f()\n";
}

template<typename T, int N, typename std::enable_if<N == 2, void>::type* = nullptr>
inline void A<T, N>::g()
{
    std::cout << "g()\n";
}

int main(int argc, char *argv[])
{
    A<float, 2> obj;
    obj.f();
    obj.g();

    return 0;
}

当我尝试编译它时,我收到一个关于有 3 个模板参数而不是 2 个的错误。然后,经过一番尝试,我决定改变g()的定义里面A本身,就像这样:

#include <type_traits>
#include <iostream>

template<typename T, int N>
class A
{
public:
    void f(void);

    template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
    void g()
    {
        std::cout << "g()\n";
    }
};

template<typename T, int N>
inline void A<T, N>::f()
{
    std::cout << "f()\n";
}

int main(int argc, char *argv[])
{
    A<float, 2> obj;
    obj.f();
    obj.g();

    return 0;
}

现在,神奇的是一切都正常了。但我的问题是为什么?难道编译器没有看到在类定义中我试图内联一个也依赖于 3 个模板参数的成员函数吗?或者让我们反过来思考这个问题:如果它在里面起作用A的定义,为什么在外面不起作用?区别在哪里?不是还有3个参数吗,比什么class多+1A需要它的模板参数吗?

另外,为什么只有当我将第三个参数设置为非类型参数而不是类型参数时它才起作用?请注意,我实际上创建了一个由enable_if返回的类型的指针,并为其分配了默认值nullptr,但我发现我不能像我在这里看到的其他SO论坛帖子一样将其留在那里作为类型参数。

非常感谢,谢谢!!!


那是因为模板类中的模板函数具有two一组模板参数,而不是一个。因此,“正确”的形式是:

template<typename T, int N>
class A
{
public:
    void f(void);

    template<typename std::enable_if<N == 2, void>::type* = nullptr>
    void g(void);
};

template<typename T, int N>                                            // Class template.
template<typename std::enable_if<N == 2, void>::type* /* = nullptr */> // Function template.
inline void A<T, N>::g()
{
    std::cout << "g()\n";
}

查看实际效果here http://coliru.stacked-crooked.com/a/c0689d66b1264339.

[注意,这不是actually正确,原因在这个答案的底部解释。如果它会破裂N != 2.]

如果您愿意,请继续阅读以获得解释。


还在我这儿?好的。让我们检查一下每种情况,好吗?

  1. 定义A<T, N>::g()外部A:

    template<typename T, int N>
    class A
    {
    public:
        void f(void);
        void g(void);
    };
    
    template<typename T, int N, typename std::enable_if<N == 2, void>::type* = nullptr>
    inline void A<T, N>::g()
    {
        std::cout << "g()\n";
    }
    

    在这种情况下,A<T, N>::g()的模板声明不匹配A的模板声明。因此,编译器会发出错误。此外,g()本身没有模板化,因此模板不能在不改变的情况下分为类模板和函数模板A的定义。

    template<typename T, int N>
    class A
    {
    public:
        void f(void);
    
        // Here...
        template<typename std::enable_if<N == 2, void>::type* = nullptr>
        void g(void);
    };
    
    // And here.
    template<typename T, int N>                                            // Class template.
    template<typename std::enable_if<N == 2, void>::type* /* = nullptr */> // Function template.
    inline void A<T, N>::g()
    {
        std::cout << "g()\n";
    }
    
  2. 定义A<T, N>::g() inside A:

    template<typename T, int N>
    class A
    {
    public:
        void f(void);
    
        template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
        void g()
        {
            std::cout << "g()\n";
        }
    };
    

    在这种情况下,由于g()是内联定义的,它隐式地具有A的模板参数,无需手动指定。所以,g()实际上是:

    // ...
        template<typename T, int N>
        template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
        void g()
        {
            std::cout << "g()\n";
        }
    // ...
    

在这两种情况下,对于g()为了拥有自己的模板参数,同时作为模板类的成员,函数模板参数必须与类模板参数分开。否则,函数的类模板将与类不匹配。


现在我们已经介绍了这一点,我应该指出 SFINAE 只关注即时模板参数。因此对于g()与 SFINAE 一起使用N, N需要是它的模板参数;否则,如果您尝试调用,则会收到错误消息,例如,A<float, 3>{}.g()。如有必要,这可以通过中介来完成。

此外,您还需要提供以下版本g()N != 2。这是因为 SFINAE 仅当该函数至少有一个有效版本时才适用;如果没有版本g()可以调用,然后将发出错误并且不会执行 SFINAE。

template<typename T, int N>
class A
{
public:
    void f(void);

    // Note the use of "MyN".
    template<int MyN = N, typename std::enable_if<MyN == 2, void>::type* = nullptr>
    void g(void);

    // Note the "fail condition" overload.
    template<int MyN = N, typename std::enable_if<MyN != 2, void>::type* = nullptr>
    void g(void);
};

template<typename T, int N>
template<int MyN /*= N*/, typename std::enable_if<MyN == 2, void>::type* /* = nullptr */>
inline void A<T, N>::g()
{
    std::cout << "g()\n";
}

template<typename T, int N>
template<int MyN /*= N*/, typename std::enable_if<MyN != 2, void>::type* /* = nullptr */>
inline void A<T, N>::g()
{
    std::cout << "()g\n";
}

如果这样做,我们可以通过让中介承担繁重的工作来进一步简化事情。

template<typename T, int N>
class A
{
public:
    void f(void);

    template<bool B = (N == 2), typename std::enable_if<B, void>::type* = nullptr>
    void g(void);

    template<bool B = (N == 2), typename std::enable_if<!B, void>::type* = nullptr>
    void g(void);
};

// ...

查看实际效果here http://coliru.stacked-crooked.com/a/5b2b888c8a77889d.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 SFINAE (enable_if) 在类定义内部起作用,但在外部不起作用 的相关文章

  • ROWNUM 的 OracleType 是什么

    我试图参数化所有现有的 sql 但以下代码给了我一个问题 command CommandText String Format SELECT FROM 0 WHERE ROWNUM lt maxRecords command CommandT
  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • C++ 求二维数组每一行的最大值

    我已经设法用这个找到我的二维数组的每一行的最小值 void findLowest int A Cm int n int m int min A 0 0 for int i 0 i lt n i for int j 0 j lt m j if
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • Cygwin 下使用 CMake 编译库

    我一直在尝试使用 CMake 来编译 TinyXML 作为一种迷你项目 尝试学习 CMake 作为补充 我试图将其编译成动态库并自行安装 以便它可以工作 到目前为止 我已经设法编译和安装它 但它编译成 dll 和 dll a 让它工作的唯一
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • c# Asp.NET MVC 使用FileStreamResult下载excel文件

    我需要构建一个方法 它将接收模型 从中构建excel 构建和接收部分完成没有问题 然后使用内存流导出 让用户下载它 不将其保存在服务器上 我是 ASP NET 和 MVC 的新手 所以我找到了指南并将其构建为教程项目 public File
  • A* 之间的差异 pA = 新 A;和 A* pA = 新 A();

    在 C 中 以下两个动态对象创建之间的确切区别是什么 A pA new A A pA new A 我做了一些测试 但似乎在这两种情况下 都调用了默认构造函数 并且仅调用了它 我正在寻找性能方面的任何差异 Thanks If A是 POD 类
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 用 C 实现 Unix shell:检查文件是否可执行

    我正在努力用 C 语言实现 Unix shell 目前正在处理相对路径的问题 特别是在输入命令时 现在 我每次都必须输入可执行文件的完整路径 而我宁愿简单地输入 ls 或 cat 我已经设法获取 PATH 环境变量 我的想法是在 字符处拆分
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • GDK3/GTK3窗口更新的精确定时

    我有一个使用 GTK 用 C 语言编写的应用程序 尽管该语言对于这个问题可能并不重要 这个应用程序有全屏gtk window与单个gtk drawing area 对于绘图区域 我已经通过注册了一个刻度回调gtk widget add ti
  • Bing 地图运行时错误 Windows 8.1

    当我运行带有 Bing Map 集成的 Windows 8 1 应用程序时 出现以下错误 Windows UI Xaml Markup XamlParseException 类型的异常 发生在 DistanceApp exe 中 但未在用户
  • 如何在 C# 中播放在线资源中的 .mp3 文件?

    我的问题与此非常相似question https stackoverflow com questions 7556672 mp3 play from stream on c sharp 我有音乐网址 网址如http site com aud
  • 更改显示的 DPI 缩放大小使 Qt 应用程序的字体大小渲染得更大

    我使用 Qt 创建了一些 GUI 应用程序 我的 GUI 应用程序包含按钮和单选按钮等控件 当我运行应用程序时 按钮内的按钮和字体看起来正常 当我将显示器的 DPI 缩放大小从 100 更改为 150 或 200 时 无论分辨率如何 控件的

随机推荐

  • MySQL存储过程错误意外字符“:”

    我有以下语句来创建存储过程 但我不断收到 位置 835 处出现意外字符 错误 语句中唯一的冒号位于 start loop 循环中 为什么我会收到此错误以及如何修复 DELIMITER CREATE DEFINER root localhos
  • 批处理文件中的 Powershell - 如何转义元字符?

    运行 Windows 7 当我复制文件在例程期间到外部磁盘文件备份 我使用 Powershell v2 从批处理文件 在副本文件上重新创建原始文件的所有时间戳 以下代码在大多数情况下都能成功运行 但并非总是如此 SET file 1 SET
  • Laravel Eloquent - 附加与同步

    有什么区别attach and sync 在 Laravel 4 的 Eloquent ORM 中 我试图环顾四周 但什么也没找到 附 处理多对多关系时插入相关模型 不需要数组参数 Example user User find 1 user
  • 如何在java中使用模式匹配器?

    假设字符串是我想提取xyz从字符串中出来 我用了 Pattern titlePattern Pattern compile lttitle gt s s lt title gt Matcher titleMatcher titlePatte
  • 从 s3 静态网站中的按钮触发 lambda

    我有一个静态网站 其中有一个包含几个字段的表单 CloudFront 位于将流量路由到站点的存储桶的前面 所涉及的表单自然有一个 POST 到 的按钮 有没有办法让按钮上的点击触发带有表单字段内容的 lambda 函数 提前致谢 API G
  • 破坏/分解函数的函数

    我以前有过 here https stackoverflow com questions 4920610 c class function in assembly 已经表明 C 函数不容易用汇编表示 现在我有兴趣以一种或另一种方式阅读它们
  • 在 WPF 中创建复合位图图像

    我有三个位图图像 我想将它们拼接在一起以创建合成图像 要拼接在一起的三个图像按以下方式对齐 这些图像的类型为 System Windows Media Imaging BitmapImage 我看过以下内容solution https st
  • 如何在 C++ 中的特定坐标处将字符串打印到控制台?

    我正在尝试在控制台中的指定坐标处打印字符 到目前为止我一直在使用非常丑陋的printf 033 d dH s n 2 2 str 但我只想问 C 是否有其他方法可以做到这一点 问题甚至不在于它丑陋 当我试图让自己成为一个更漂亮的函数时 问题
  • 尝试旋转和变换 SVG 路径 - 我需要三角学计算吗?

    我正在尝试使用鼠标 SVG 路径进行操作 该路径代表电子电阻器的符号 该符号需要使用 引线 末端进行操作 如果您可以想象真实的电阻器 因此 我试图实现拖动第一个点周围 第二个点仍然存在 并且当在新坐标上拖动第一个点时 路径的所有点都按比例表
  • python列表理解和extend() [重复]

    这个问题在这里已经有答案了 深入学习 Python 2 7 1 但未能理解这一点 几个小时 gt gt gt a 1 2 gt gt gt b 3 4 gt gt gt gt gt gt a extend b 0 gt gt gt a 1
  • 将日期时间模型属性显示为短日期时间字符串

    我是 MVC2 新手 并且遇到格式问题 我的 Employee 模型中有一个 DateTime 属性 我希望与短日期时间一起显示 然而 这似乎不是正确的方法 1 div class editor field 2 3 4 div 第 2 行抛
  • Rails 数据库默认值和布尔字段的模型验证

    在 Rails 模型中我有一个属性is subscriber 当我构建数据库迁移以将此列添加到数据库时 我指定默认值为 false t boolean is subscriber default gt false 我还在模型中指定了该属性需
  • 如何在 Powershell 中为特定命令设置别名?

    有一个命令我经常输入 java jar foo param1 param2 param n 如何创建命令别名来运行此命令 如下所示 launch foo param3 where launch foo被翻译成完整的命令 你必须创建一个函数
  • 表视图重新加载部分崩溃

    我有一个包含 4 个部分的表格视图 每个部分有 1 2 个表格视图单元格 第一个单元格有一个 uiswitch 作为附件视图 控制应用程序的颜色主题 在白天模式和夜间模式之间切换 一旦按下开关 就会调用一个函数 更改导航栏的颜色和背景颜色
  • Docker容器CPU使用率监控

    根据 docker 的文档 我们可以通过以下方式获取 docker 容器的 CPU 使用率码头工人统计命令 CPU 列将给出容器正在使用的主机 CPU 的百分比 假设我限制容器使用 50 的主机单个 CPU 我可以通过 cpus 0 5 选
  • 在没有条件的情况下,如何使查询不返回任何内容?

    相当简单 我有一对多 多对一关系 我想查询它 但是 当未提供任何 WHERE 子句信息时 我不希望返回任何结果 简单来说 如何使查询变得非贪婪 您可以添加一个始终为 false 的 where 子句 并附加您想要用 OR 提供的条件 sel
  • 多个结构体,需要在方法中访问相同的字段

    我目前尝试用 C 语言编写一些简单的控制台游戏来娱乐 为此 我需要能够在 嗯 C 中打印类似窗口的结构 我想使用通用渲染方法 让我们称之为frame render 渲染所有不同的 ui 元素 现在的问题是 如何解决这个问题 给定场景 The
  • 计算 IEnumerable 的计数(非泛型)

    谁能帮我一个Count扩展方法为IEnumerable 非通用接口 我知道LINQ不支持它 但是如何手动编写呢 yourEnumerable Cast Count 关于性能的评论 我认为这是过早优化的一个很好的例子 但你可以看一下 stat
  • 使用 Ctrl+v 或右键单击 -> 粘贴检测粘贴的文本

    使用 JavaScript 如何检测用户将哪些文本粘贴到文本区域 您可以使用粘贴事件来检测大多数浏览器中的粘贴 尤其是 Firefox 2 当您处理粘贴事件时 记录当前选择 然后设置一个简短的计时器 在粘贴完成后调用一个函数 然后 该函数可
  • 为什么 SFINAE (enable_if) 在类定义内部起作用,但在外部不起作用

    过去几个小时我一直在努力解决这个非常奇怪的问题 在解决了 SFINAE 的 5 6 个其他问题之后 因为我是新手 基本上在下面的代码中我想要f 适用于所有可能的模板实例化 但有g 仅当N 2 include