根据 std::tuple 的大小更改函数的返回类型

3

问题

以下是我面临的一个简化、捏造的示例问题。实际上,我需要一个能够容纳任意数量项目并在需要时返回这些项目的对象。

template<typename... Ts>
class Foo {
public:
    Foo(Ts... args) :
        mArgs{std::forward<Ts>(args)} {
    }

    std::tuple<Ts...> getArgs() const {
        return mArgs;
    }

private:
    std::tuple<Ts...> mArgs;
};

当参数大于1时,这个功能可以正常工作。

Foo<int, int> f{1, 2};
auto result = f.getArgs(); // result is a tuple containing two ints.

然而,如果args == 1,我更倾向于不从getArgs获得一个元组。
Foo<int> f{1};
auto result = f.getArgs(); // result is a tuple, but I want it to be an int.

问题

是否有一种方法,也许使用 SFINAE,来定义另一个getArgs函数当 args == 1 时使用?类似于(这显然是非常错误的):

template<typename = std::enable_if_t<std::tuple_size_v<Ts...> == 1>>
??? getArgs() const {
    return mArgs;
}

以下是一些明显的问题:

  1. 我不确定 std::tuple_size_v<Ts...> 是否有效。
  2. 我不知道新的返回类型将是什么。也许在这里使用 std::tuple_element 会有用。

你只是在询问 std::conditional_t 吗? - Davis Herring
2个回答

3
另一种方法:使用sizeof...(T) == 1来确定是否仅使用了单个类型。然后使用auto返回类型和if constexpr进行其余操作。

template<typename... T>
class Foo {
public:
    Foo(T&&... t)
        : mArgs{std::forward<T>(t)...} {
        
    }
    
    auto getArgs() const {
        if constexpr (sizeof...(T) == 1) {
            return std::get<0>(mArgs);
        } else {
            return mArgs;
        }
    }
  
private:
    std::tuple<T...> mArgs;
};

使用示例:

auto foo_i = Foo{11};
auto foo_c = Foo{'c'};
auto foo_ic = Foo{11, 'c'};

int i2 = foo_i.getArgs();
char c2 = foo_c.getArgs();
std::tuple<int, char> ic2 = foo_ic.getArgs();

演示现场


2

你可以使用给定一组类型的部分特化来实现这一点,无需使用SFINAE。以下是如何在int示例中实现:

#include <stdio.h>
#include <tuple>

template<typename... Ts>
struct storage {
    typedef std::tuple<Ts...> type;
};

template<typename T>
struct storage<T> {
    typedef T type;
};

template<typename... Ts>
class Foo {
public:
    Foo(Ts... args) :
        mArgs{std::forward<Ts>(args)...} {
    }

    typename storage<Ts...>::type getArgs() const {
        return mArgs;
    }

private:
    typename storage<Ts...>::type mArgs;
};

int main(int argc, char **argv)
{
    Foo<int, int> f1{1, 2};
    Foo<int> f2{1};

    std::tuple<int, int> args1 = f1.getArgs();
    printf("args1 = (%d, %d)\n", std::get<0>(args1), std::get<1>(args1));
    int args2 = f2.getArgs();
    printf("args2 = %d\n", args2);

    return 0;
}

第二个 storage 不应该被改成更通用的 T模板,而不是具体的int` 吗? - Remy Lebeau
好主意@RemyLebeau,那确实是更好的解决方案。我会更新答案。 - Jon Reeves

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接