代码之家  ›  专栏  ›  技术社区  ›  Zistack

如何构造一个类型特征来判断一个类型的私有方法是否可以在另一个类型的构造函数中调用?

  •  2
  • Zistack  · 技术社区  · 6 年前

    我使用C++ 17。我有如下代码:

    #include <type_traits>
    
    template <typename T>
    struct Fooer
    {
        Fooer (T & fooable)
        {
            fooable . foo ();
        }
    };
    
    template <typename T>
    Fooer (T & fooable) -> Fooer <T>;
    
    struct Fooable
    {
    private:
    
        void
        foo ();
    
        friend struct Fooer <Fooable>;
    };
    
    struct NotFooable
    {
    };
    

    我想实现一个类型特征,它可以判断一个类型是否是“可食的”。

    我查不出有没有办法 foo () 因为它是一个私有方法。这也不能告诉我 Fooer 的构造函数可以调用该方法。

    // Checking for the foo method doesn't work.
    
    template <typename T, typename = void>
    struct HasFoo;
    
    template <typename T, typename>
    struct HasFoo : std::false_type
    {
    };
    
    template <typename T>
    struct HasFoo
    <
        T,
        std::enable_if_t
        <
            std::is_convertible_v <decltype (std::declval <T> () . foo ()), void>
        >
    >
    :   std::true_type
    {
    };
    
    // Both of these assertions fail.
    static_assert (HasFoo <Fooable>::value);
    static_assert (HasFoo <NotFooable>::value);
    

    Fooer <T> 可通过 std::is_constructible ,因为 std::是可构造的吗 定义 Fooer <T> fooer (std::declval <T> ()) .

    // Checking constructibility doesn't work either.
    
    template <typename T, typename = void>
    struct CanMakeFooer;
    
    template <typename T, typename>
    struct CanMakeFooer : std::false_type
    {
    };
    
    template <typename T>
    struct CanMakeFooer
    <
        T,
        std::enable_if_t <std::is_constructible_v <Fooer <T>, T &>>
    >
    :   std::true_type
    {
    };
    
    // Neither of these assertions fail.
    static_assert (CanMakeFooer <Fooable>::value);
    static_assert (CanMakeFooer <NotFooable>::value);
    

    如果我真的尝试调用构造函数,我会得到预期的错误,尽管这并不能让我更接近实现类型特征。

    void
    createFooer ()
    {
        Fooable fooable;
        NotFooable not_fooable;
    
        // This works fine.
        { Fooer fooer (fooable); }
    
        // This correctly generates the compiler error: no member named 'foo' in
        // 'NotFooable'
        { Fooer fooer (not_fooable); } 
    }
    

    我希望避免将类型trait声明为可Fooable类型的朋友,并避免将“foo”公开。

    如果我能检查一下 定义

    0 回复  |  直到 6 年前
        1
  •  3
  •   aschepler    6 年前

    这里的问题是 Fooer 不是“斯芬纳友好”。它有一个要求 可以打电话 fooable.foo() ,但是就C++而言,声明 Fooer(T &);

    #include <utility>
    
    template <typename T>
    struct Fooer
    {
        template <typename U = T, typename Enable =
                    std::void_t<decltype(std::declval<U&>().foo())>>
        Fooer (T & fooable)
        {
            fooable . foo ();
        }
    };
    

    这将变得更容易和更清晰的C++ 20约束:

    // C++20 code
    template <typename T>
    struct Fooer
    {
         Fooer (T & fooable) requires requires { fooable.foo(); }
         {
             fooable . foo ();
         }
    };
    

    有了这些变化,你的 CanMakeFooer 应该有用。尽管可以更简单地定义它,只使用主模板,而不使用专门化:

    template <typename T>
    struct CanMakeFooer :
        public std::bool_constant<std::is_constructible_v<Fooer<T>, T&>>
    {};
    

    Demo on coliru.

        2
  •  5
  •   Indiana Kernick Michael Durrant    6 年前

    foo() 是《世界银行宣言》的一部分 Fooer 使构造器变得友好。您可以使用构造函数模板和需求的默认模板参数来实现这一点。这意味着 HasFoo 富尔 T 也不用担心 功能。

    template <typename T>
    struct Fooer {
      template <typename U, typename = std::void_t<
        decltype(std::declval<U &>().foo()),
        std::enable_if_t<std::is_same_v<T, U>>
      >>
      explicit Fooer(U &fooable) {
        fooable.foo();
      }
    };
    
    template <typename U>
    Fooer(U &) -> Fooer<U>;
    
    template <typename T>
    struct HasFoo : std::bool_constant<
      std::is_constructible_v<Fooer<T>, T &>
    > {};
    
    struct Fooable {
    private:
      void foo() {}
    
      friend struct Fooer<Fooable>;
    };
    
    struct NotFooable {};
    
    static_assert(HasFoo<Fooable>::value);
    static_assert(!HasFoo<NotFooable>::value);
    
        3
  •  1
  •   Gerard097    6 年前

    Fooer (T& fooable)
    {
        if constexpr (has_foo<T>(0)) {
            cout << "Yes foo" << endl;
            fooable.foo();
        }
        else {
            cout << "No foo" << endl;
        }
    }
    
    template<class Y>
    static constexpr auto has_foo(Y*) -> decltype(std::declval<Y>().foo(), bool()) {
        return true;
    }
    
    template<class Y>
    static constexpr bool has_foo(...) {
        return false;
    }
    

    https://wandbox.org/permlink/5GGY89YVLsfqeZ2A