Sfinae超负荷在不同尺寸的元素上

发布于 2025-01-23 06:37:56 字数 5938 浏览 2 评论 0原文

考虑一个包含两种类型的结构 - firstObjectssecondObjects,它们都是std :: tuple<>>,例如,例如

struct Objects
{
    using FirstObjects = std::tuple<int, int>;
    using SecondObjects = std::tuple<int>;
}; 

:我们添加以下枚举:

enum class ObjectCategory
{
    FIRST,
    SECOND
};

现在考虑上述类型上的以下类,如上所述(唯一的合同是它具有firstObjects> SecondObjects,并且它们为std :: tuple):

template <typename T>
class SomeClass
{
public:
    using FirstObjects = typename T::FirstObjects;
    using SecondObjects = typename T::SecondObjects;
    
    template <std::size_t Idx>
    using FirstObject = typename std::tuple_element<Idx, FirstObjects>::type;
    template <std::size_t Idx>
    using SecondObject = typename std::tuple_element<Idx, SecondObjects>::type;        
    
    template <ObjectCategory Category, std::size_t Idx>
    using ObjectType = std::conditional_t<Category == ObjectCategory::FIRST, FirstObject<Idx>, SecondObject<Idx>>;
    
    template <ObjectCategory Category, std::size_t Idx>
    std::enable_if_t<Category == ObjectCategory::FIRST, const ObjectType<Category, Idx>>& getObject()
    {
        return std::get<Idx>(firstObjects_);
    }

    template <ObjectCategory Category, std::size_t Idx>
    std::enable_if_t<Category == ObjectCategory::SECOND, const ObjectType<Category, Idx>>& getObject()
    {
        return std::get<Idx>(secondObjects_);
    }    
    
    template <ObjectCategory Category, std::size_t Idx>
    void doSomething()
    {
        const ObjectType<Category, Idx>& obj = getObject<Category, Idx>();
    }
    
private:
    FirstObjects firstObjects_;
    SecondObjects secondObjects_;
};

简而> t :: firstObjects 和secondObjects _ type t :: secondObjects,并且具有dosomething()函数根据Objectcategory选择,返回i-th对象的sfinae-overpraded getObject() getter方法。

我现在遇到的问题是:

int main()
{
    struct Objects
    {
        using FirstObjects = std::tuple<int, int>;
        using SecondObjects = std::tuple<int>;
    };    
    SomeClass<Objects> object{};
    object.doSomething<ObjectCategory::FIRST, 0>(); // <------ works fine
    object.doSomething<ObjectCategory::FIRST, 1>(); // <------ compiler error
}

最后一行使编译器抱怨:

/usr/include/c++/4.9/tuple: In instantiation of 'struct std::tuple_element<1ul, std::tuple<int> >':
70:114:   required by substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
87:42:   required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50:   required from here
/usr/include/c++/4.9/tuple:682:12: error: invalid use of incomplete type 'struct std::tuple_element<0ul, std::tuple<> >'
     struct tuple_element<__i, tuple<_Head, _Tail...> >
            ^
In file included from /usr/include/c++/4.9/tuple:38:0,
                 from 4:
/usr/include/c++/4.9/utility:85:11: error: declaration of 'struct std::tuple_element<0ul, std::tuple<> >'
     class tuple_element;
           ^
 In substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]':
87:42:   required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50:   required from here
70:114: error: no type named 'type' in 'struct std::tuple_element<1ul, std::tuple<int> >'

似乎不喜欢secondObjects只有一个元素,因此std :: get&lt; 1&gt;( secondObjects _)不起作用?但是,为什么这甚至是首先发生的,因为我在dosomething on objectCategory :: first而不是objectcategory :: second

如何解决?

------编辑1 ------

解决方案@taekahn指出的是要更改getObject()方法如下:

    template <ObjectCategory Category, std::size_t Idx>
    const auto& getObject()
    {
        return std::get<Idx>(firstObjects_);
    }

    template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::SECOND>>
    const auto& getObject()
    {
        return std::get<Idx>(secondObjects_);
    }

似乎有效,因为编译器未评估编译器/在使用关键字auto 而不是显式类型时,检查已删除的方法的返回类型的有效性。 但这只是猜想 - 真的很喜欢一个知识渊博的C ++从业者来体重!

------编辑2 ----------------------------------------------------------------------------------------------》

迄今为止尝试进行

    object.doSomething<ObjectCategory::SECOND, 0>();

战争时,是将第一个Getter函数修改为

    template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::FIRST>, bool = 0 /* dummy param */>
    auto& getObject()
    {
        return std::get<Idx>(firstObjects_);
    }

添加Sfinae以及虚拟模板最后,ARG允许超载。

Consider a struct that contains two types - FirstObjects and SecondObjects, which are both std::tuple<>, e.g. something like:

struct Objects
{
    using FirstObjects = std::tuple<int, int>;
    using SecondObjects = std::tuple<int>;
}; 

For convenience we add the following enum:

enum class ObjectCategory
{
    FIRST,
    SECOND
};

Now consider the following class that is templated on a type such as Objects as described above (the only contract is that it have FirstObjects and SecondObjects and that they be std::tuple):

template <typename T>
class SomeClass
{
public:
    using FirstObjects = typename T::FirstObjects;
    using SecondObjects = typename T::SecondObjects;
    
    template <std::size_t Idx>
    using FirstObject = typename std::tuple_element<Idx, FirstObjects>::type;
    template <std::size_t Idx>
    using SecondObject = typename std::tuple_element<Idx, SecondObjects>::type;        
    
    template <ObjectCategory Category, std::size_t Idx>
    using ObjectType = std::conditional_t<Category == ObjectCategory::FIRST, FirstObject<Idx>, SecondObject<Idx>>;
    
    template <ObjectCategory Category, std::size_t Idx>
    std::enable_if_t<Category == ObjectCategory::FIRST, const ObjectType<Category, Idx>>& getObject()
    {
        return std::get<Idx>(firstObjects_);
    }

    template <ObjectCategory Category, std::size_t Idx>
    std::enable_if_t<Category == ObjectCategory::SECOND, const ObjectType<Category, Idx>>& getObject()
    {
        return std::get<Idx>(secondObjects_);
    }    
    
    template <ObjectCategory Category, std::size_t Idx>
    void doSomething()
    {
        const ObjectType<Category, Idx>& obj = getObject<Category, Idx>();
    }
    
private:
    FirstObjects firstObjects_;
    SecondObjects secondObjects_;
};

In brief, SomeClass hosts two member variables, firstObjects_ of type T::FirstObjects, and secondObjects_ of type T::SecondObjects, and has a doSomething() function which makes use of the SFINAE-overloaded getObject() getter method that returns the i-th object contained in firstObjects_ or in secondObjects_ depending on the ObjectCategory chosen.

The problem I have now is:

int main()
{
    struct Objects
    {
        using FirstObjects = std::tuple<int, int>;
        using SecondObjects = std::tuple<int>;
    };    
    SomeClass<Objects> object{};
    object.doSomething<ObjectCategory::FIRST, 0>(); // <------ works fine
    object.doSomething<ObjectCategory::FIRST, 1>(); // <------ compiler error
}

The last line makes the compiler complain:

/usr/include/c++/4.9/tuple: In instantiation of 'struct std::tuple_element<1ul, std::tuple<int> >':
70:114:   required by substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
87:42:   required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50:   required from here
/usr/include/c++/4.9/tuple:682:12: error: invalid use of incomplete type 'struct std::tuple_element<0ul, std::tuple<> >'
     struct tuple_element<__i, tuple<_Head, _Tail...> >
            ^
In file included from /usr/include/c++/4.9/tuple:38:0,
                 from 4:
/usr/include/c++/4.9/utility:85:11: error: declaration of 'struct std::tuple_element<0ul, std::tuple<> >'
     class tuple_element;
           ^
 In substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]':
87:42:   required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50:   required from here
70:114: error: no type named 'type' in 'struct std::tuple_element<1ul, std::tuple<int> >'

It seems like it is not liking the fact that SecondObjects only has one element and therefore std::get<1>(secondObjects_) is not working? But why is this even happening in the first place since I'm calling doSomething on ObjectCategory::FIRST and not ObjectCategory::SECOND?

How can this be solved?

------ EDIT 1 ------

Solution pointed out by @Taekahn is to change the getObject() method as follows:

    template <ObjectCategory Category, std::size_t Idx>
    const auto& getObject()
    {
        return std::get<Idx>(firstObjects_);
    }

    template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::SECOND>>
    const auto& getObject()
    {
        return std::get<Idx>(secondObjects_);
    }

It seems like the above works because the compiler does not evaluate / check the validity of the return type of a method that has been SFINAE'd out when one uses the keyword auto instead of the explicit type.
But this is only conjecture - would really appreciate a more knowledgeable C++ practitioner to weigh in here!

------ EDIT 2 ------

The above results in ambiguity when trying to do

    object.doSomething<ObjectCategory::SECOND, 0>();

The WAR I've found so far is to modify the first getter function to

    template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::FIRST>, bool = 0 /* dummy param */>
    auto& getObject()
    {
        return std::get<Idx>(firstObjects_);
    }

i.e. to add the SFINAE as well as a dummy template arg at the end to allow overloading.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文