AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79594192
Accepted
zhanginou
zhanginou
Asked: 2025-04-27 01:12:29 +0800 CST2025-04-27 01:12:29 +0800 CST 2025-04-27 01:12:29 +0800 CST

类型特征用于检查函数是否可以使用给定类型进行编译

  • 772

我有两种类型,,,CustomNotCompatibleWithRun和CustomCompatibleWithRun一个run()函数:

#include <iostream>
#include <type_traits>

template <typename T_>
struct CustomNotCompatibleWithRun { using T = T_; };

template <typename T_>
struct CustomCompatibleWithRun {
    using T = T_; 
    
    CustomCompatibleWithRun operator+(const CustomCompatibleWithRun& other)
    {
        CustomCompatibleWithRun res;
        res.a = a + other.a;
        res.b = b + other.b;
        return res;
    }
    
    void print() const
    {
        std::cout << "a, b = " << a << ", " << b << "\n";
    }
    
    T a;
    T b;
};

template <typename T>
T run(T a, T b) { return a+b; }

我想编写一个类型特征,IsTypeCompatibleWithRun用于检查给定类型Type(其唯一约定是具有类型别名Type::T)是否可以在函数中使用run()。即:

int main() {
    using T = float;
    CustomCompatibleWithRun<T> obj1{.a = 1.f, .b = 2.f};
    CustomCompatibleWithRun<T> obj2{.a = 2.f, .b = 3.f};
    obj1.print(); // a, b = 1, 2
    obj2.print(); // a, b = 2, 3
    const auto obj3 = obj1 + obj2;
    obj3.print(); // a, b = 3, 5
    const auto obj4 = run(obj1, obj2);
    obj4.print(); // a, b = 3, 5
    CustomNotCompatibleWithRun<T> obj5{};
    CustomNotCompatibleWithRun<T> obj6{};
    // const auto obj7 = obj5 + obj6; // does not compile
    
    std::cout << "IsTypeCompatibleWithRun<CustomCompatibleWithRun>::value: " << IsTypeCompatibleWithRun<CustomCompatibleWithRun<T>>::value << "\n"; // should print 1
    std::cout << "IsTypeCompatibleWithRun<CustomNotCompatibleWithRun>::value: " << IsTypeCompatibleWithRun<CustomNotCompatibleWithRun<T>>::value << "\n"; // should print 0
}

我尝试了以下三个选项:

template <typename Type, typename = void>
struct IsTypeCompatibleWithRun : std::false_type {};

// Option 1
template <typename Type>
struct IsTypeCompatibleWithRun<Type, decltype(void(run<typename Type::T>(std::declval<typename Type::T>(), std::declval<typename Type::T>())))> : std::true_type {};

// Option 2
template <typename Type>
struct IsTypeCompatibleWithRun<Type, decltype(void(std::declval<Type>() + std::declval<Type>()))> : std::true_type {};

// Option 3
template <typename Type>
struct IsTypeCompatibleWithRun<Type, std::void_t<decltype(run(std::declval<Type>(), std::declval<Type>()))>> : std::true_type {};

只有选项 2 有效——据我理解,这是因为我们强制将+运算符置于类型特征本身内部,而对于选项 1 和 3,只run()检查函数的签名?然而,我对选项 2 并不满意,因为它需要将实际的函数体重复run()到类型特征本身中。在这个简单的例子中,这样做没问题,但在我的应用程序中,函数体run()要复杂得多,不可能在类型特征内部重复它。

然后我尝试将签名更改run()为

template <typename T>
auto run(T a, T b)

但代码根本编译不出来。于是我尝试添加一个尾随返回类型

template <typename T>
auto run(T a, T b) -> decltype(a+b)

现在选项 3 可以工作,但选项 1 不行。这是为什么呢?

无论哪种方式,上述操作都涉及重复a+b尾随返回类型中的部分,因此这对我来说不是一个解决方案。

在 C++ 14 中,有没有一种方法可以在不改变run()而仅依赖run()(并且不重复其函数体)的情况下实现这种类型特征?

c++
  • 2 2 个回答
  • 81 Views

2 个回答

  • Voted
  1. Best Answer
    Alex
    2025-04-27T01:20:14+08:002025-04-27T01:20:14+08:00

    您看到的是 C++14 中 SFINAE (替换失败不是错误) 的限制。它仅在模板替换期间检查函数签名,而不检查函数体(如果这样做合理的话)。

    如果不改变run()或重复其主体,就无法实现这种类型特征。

    希望这有帮助

    • 1
  2. Michaël Roy
    2025-04-27T16:21:31+08:002025-04-27T16:21:31+08:00

    稍微解释一下你对上面 Alex 的回答的问题。
    这个限制可以在 C++ 20 中通过概念来克服。这意味着在 C++ 14 中,使用 option1 并在 run() 的声明中使用 std::enable_if<> 来进一步限制对 run() 的访问是完全可能的。虽然在 C++ 14 中看起来会有点混乱,但可以实现。

    例子:

    #include <iostream>
    #include <type_traits>
    
    // these will help to reduce clutter a bit
    template <typename... Ts>
    struct make_void {
        typedef void type;
    };
    template <typename... Ts>
    using void_t = typename make_void<Ts...>::type;
    
    // inner_type compatibility type_traits
    template <typename T, typename = void>
    struct can_add : std::false_type {};
    
    template <typename T>
    struct can_add<T,
                   std::enable_if_t<std::is_same<
                       T, decltype(std::declval<T>() + std::declval<T>())>::value>>
        : std::true_type {};
    
    // outer type compatibility type_traits
    template <typename T, typename = void>
    struct has_typedef_t : std::false_type {};
    
    template <typename T>
    struct has_typedef_t<T, void_t<typename T::T>> : std::true_type {};
    
    template <typename T, typename = void>
    struct has_run : std::false_type {};
    
    template <typename T>
    struct has_run<T, void_t<decltype(std::declval<T>().run(std::declval<T>()))>>
        : std::true_type {};
    
    template <typename T>
    struct is_run_compatible
        : std::integral_constant<bool,
                                 has_typedef_t<T>::value && has_run<T>::value> {};
    
    // types under test
    struct bogus {};
    
    template <typename Type>
    struct not_compatible {
        using T = Type;
    };
    
    template <typename Type>
    struct compatible {
        using T = Type;
    
        // here is the correct place to chack for compatibility with Type.
        template <typename R = std::enable_if<can_add<Type>::value, compatible>>
        typename R::type run(const compatible& a) {
            return compatible{val_ + a.val_};
        }
    
        Type val_;
    };
    
    int main() {
        compatible<int> a;
        compatible<int> b;
        auto c = a.run(b);
    
        std::cout << "can_add<int>: " << can_add<int>::value << std::endl;
        std::cout << "can_add<float>: " << can_add<float>::value << std::endl;
        std::cout << "can_add<bogus>: " << can_add<bogus>::value << std::endl;
    
        std::cout << "has_typedef_t<int>" << has_typedef_t<int>::value << std::endl;
        std::cout << "has_typedef_t<not_compatible<int>>"
                  << has_typedef_t<not_compatible<int>>::value << std::endl;
        std::cout << "has_typedef_t<compatible<bogus>>"
                  << has_typedef_t<compatible<bogus>>::value << std::endl;
        std::cout << "has_typedef_t<compatible<int>>"
                  << has_typedef_t<compatible<int>>::value << std::endl;
    
        std::cout << "has_run<int>" << has_run<int>::value << std::endl;
        std::cout << "has_run<not_compatible<int>>"
                  << has_run<not_compatible<int>>::value << std::endl;
        std::cout << "has_run<compatible<bogus>>"
                  << has_run<compatible<bogus>>::value << std::endl;
        std::cout << "has_run<compatible<int>>" << has_run<compatible<int>>::value
                  << std::endl;
    
        std::cout << "is_run_compatible<int>" << has_run<int>::value << std::endl;
        std::cout << "is_run_compatible<not_compatible<int>>"
                  << is_run_compatible<not_compatible<int>>::value << std::endl;
        std::cout << "is_run_compatible<compatible<bogus>>"
                  << is_run_compatible<compatible<bogus>>::value << std::endl;
        std::cout << "is_run_compatible<compatible<int>>"
                  << is_run_compatible<compatible<int>>::value << std::endl;
    }
    

    程序输出:

    can_add<int>: 1
    can_add<float>: 1
    can_add<bogus>: 0
    has_typedef_t<int>0
    has_typedef_t<not_compatible<int>>1
    has_typedef_t<compatible<bogus>>1
    has_typedef_t<compatible<int>>1
    has_run<int>0
    has_run<not_compatible<int>>0
    has_run<compatible<bogus>>0
    has_run<compatible<int>>1
    is_run_compatible<int>0
    is_run_compatible<not_compatible<int>>0
    is_run_compatible<compatible<bogus>>0
    is_run_compatible<compatible<int>>1
    
    

    或者,您可以检查类级别的功能:

    template <typename Type, typename = std::enable_if_t<can_add<Type>::value>>
    struct compatible {
        using T = Type;
    
        // here is the correct place to chack for compatibility with Type.
        compatible run(const compatible& a) { return compatible{val_ + a.val_}; }
    
        Type val_;
    };
    
    

    您可以在此处使用此代码:https://godbolt.org/z/vnWd6vcnc

    • 0

相关问题

  • 为什么编译器在这里错过矢量化?

  • 使用带有库的 CMake 编译错误[关闭]

  • 每次我尝试运行预制时都会抛出错误

  • 如何在 C++ 中创建类似于 std::byte 的八位字节类型?

  • C++17 中 std::byte 只能按位运算?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve