特质实现给出了Clang和G 的不同结果,这是正确的

Trait implementation gives different results with clang and g++, which is right?

本文关键字:结果 实现 Clang      更新时间:2023-10-16

我开始慢慢填补我对C 模板的知识差距,并且在阅读了很多有关如何处理错误之前,在编译器真正进入模板代码的主体之前,我出现了使用以下结构来检查对象是否提供了我需要的接口。

在模型类中可见所需的接口。

#include <iostream>
#include <type_traits>
template <typename T>
struct is_model {
    private:
        template <typename B, typename A> struct size_t_allowed;
        template <typename B> struct size_t_allowed<B, size_t>{};
        template <typename B, typename A> struct double_allowed;
        template <typename B> struct double_allowed<B, double>{};
        template <typename Z> static auto test(const Z* z) -> decltype(
                size_t_allowed<size_t,decltype(z->getS())>(), 
                size_t_allowed<size_t,decltype(z->getA())>(),
                double_allowed<double,decltype(z->getTransitionProbability(0,0,0))>(),
                double_allowed<double,decltype(z->getExpectedReward(0,0,0))>(),
                std::true_type{} );
        template <typename> static auto test(...) -> std::false_type;
    public:
        enum { value = std::is_same<decltype(test<T>(0)), std::true_type>::value };
};
struct Model {
    size_t getS() const { return 0;}
    size_t getA() const { return 0;}
    double getTransitionProbability(size_t, size_t, size_t) const {return 0.0;}
    double getExpectedReward(size_t, size_t, size_t) const {return 0.0;}
};
template <typename M>
void algorithm(M, typename std::enable_if<is_model<M>::value>::type * = nullptr) {
    std::cout << "Algorithm has been performed.n";
}
int main() {
    std::cout << is_model<int>::value << "n";
    std::cout << (is_model<Model>::value ? "Yes" : "No" ) << "n";
    Model m;
    algorithm(m);
    return 0;
}

我的问题如下:

  • G 4.8.1正确编译了代码,并打印所有消息。clang 3.4实际上不会返回is_model<Model>::value的false,并且无法编译algorithm(m)。哪个是对的?
  • 目前,我只找到了检查接口返回类型的(不良)方式。有可能使事情变得更好吗?我无法为结构*_allowed使用一个模板参数,因为否则编译器会在类范围内抱怨专用结构。
  • 最后,我想知道如何对函数的参数进行检查,以及是否有意义。

编辑:多亏了Jarod的答案,我提高了解决方案。由于我喜欢它的清洁度,所以我仍然将所有内容都放入一个课程。此外,我发现了Clang的问题:由于某种原因,在这种情况下,它无法正确解析std::true_type{},而std::declval<std::true_type>()也无法使用。用std::true_type()代替它。仍然没有任何线索,我什至试图重新安装整个过程。

template <typename T>
struct is_model {
    private:
        template<typename U, U> struct helper{};
        template <typename Z> static auto test(Z* z) -> decltype(
          helper<size_t (Z::*)() const,                       &Z::getS>(),
          helper<size_t (Z::*)() const,                       &Z::getA>(),
          helper<double (Z::*)(size_t,size_t,size_t) const,   &Z::getTransitionProbability>(),
          helper<double (Z::*)(size_t,size_t,size_t) const,   &Z::getExpectedReward>(),
                                                     std::true_type());
        template <typename> static auto test(...) -> std::false_type;
    public:
        enum { value = std::is_same<decltype(test<T>((T*)nullptr)),std::true_type>::value };
};

以下可能会有所帮助,它检查完整的签名:

#include <cstdint>
#include <type_traits>
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               
    template <typename U>                                                   
    class traitsName                                                        
    {                                                                       
    private:                                                                
        template<typename T, T> struct helper;                              
        template<typename T>                                                
        static std::uint8_t check(helper<signature, &funcName>*);           
        template<typename T> static std::uint16_t check(...);               
    public:                                                                 
        static                                                              
        constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); 
    }
DEFINE_HAS_SIGNATURE(has_getS, T::getS, size_t (T::*)() const);
DEFINE_HAS_SIGNATURE(has_getA, T::getA, size_t (T::*)() const);
DEFINE_HAS_SIGNATURE(has_getTransitionProbability, T::getTransitionProbability, double (T::*)(size_t, size_t, size_t) const);
DEFINE_HAS_SIGNATURE(has_getExpectedReward, T::getExpectedReward, double (T::*)(size_t, size_t, size_t) const);
template <typename T>
struct is_model :
    std::conditional<has_getS<T>::value
                              && has_getA<T>::value
                              && has_getTransitionProbability<T>::value
                              && has_getExpectedReward<T>::value,
                              std::true_type, std::false_type>::type
{};

测试它:

struct Model {
    size_t getS() const { return 0;}
    size_t getA() const { return 0;}
    double getTransitionProbability(size_t, size_t, size_t) const {return 0.0;}
    double getExpectedReward(size_t, size_t, size_t) const {return 0.0;}
};
static_assert(is_model<Model>::value, "it should respect contract");
static_assert(!is_model<int>::value, "it shouldn't respect contract");