具有配对参数的模板函数

时间:2014-02-13 15:45:35

标签: c++ c++11

我正在尝试创建一个C ++函数,该函数总共接受未知数量的参数,但它们始终与特定类型配对

// logically, this is what the template Pair would be
// template<int, std::string> struct Pair {};

// desired:
// accept a const char * as a first parameter, and then in pairs ...
//     integer, const char *
template <typename... Arguments> unsigned int onlyInPairs
    (const std::string name, const Arguments& ... args)  {

    const unsigned numargs = sizeof...(Arguments);
    // more magic would happen here with the parameters :)
    return numargs;
}

int _tmain(int argc, _TCHAR* argv[])
{
    // only string, [num, string] [num, string] should work

    // desire that the syntax be as simple as shown, and not require
    // extra classes to be created (like a Tuple) for each pair.

    // this should work...
    auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works

    // this should not work, as it's not number, string
    count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2); 
    return 0;
}

我查看了参数包(reference),但似乎无法将我发现的文档应用于我的特定问题。如果没有正确指定参数,我想尝试在编译时捕获问题。

目标是尽可能使用没有模板噪声的语法,因为“对”总是这样(并且程序员会知道)。所以,我们想要int,string(重复)。

理想情况下,该解决方案适用于Visual Studio 2013的C ++编译器,但我接受任何有效的答案,并演示VS C ++当前可能存在的与此问题相关的缺点。

附录 - 更多详情

正在编写的代码最终将由精通技术的人员阅读,但不是经过正式培训的C / C ++程序员(如技术支持)。因此,我们试图让它尽可能地分散注意力。可以有2 - 16个值对......所以要保持它免于分心,只需要数据。

5 个答案:

答案 0 :(得分:2)

这是一种可能性。类模板Enforce以递归方式从自身继承并在模板参数对上应用static_assert,直到挑选出不执行任何操作的特化:

#include <type_traits>
#include <string>

template<typename...Args>
struct Enforce;

template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> : Enforce<T, Args...> {
    static_assert( std::is_constructible<T, T2>::value, "Wrong T2!");
};

template<typename T>
struct Enforce<T> {
};


template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
    Enforce<std::string, Arguments...>();
}


int main()
{
    onlyInPairs("this", 1, "works", 2, "fine");
    //onlyInPairs("this", 1, "doesn't", 2, 3);
}

您可以使用递归typedef而不是递归继承。至少在gcc中,它应该编译得更快,噪音更少(警告基类中的非虚拟析构函数等)。

编辑:

这是另一个将检查结合在一起并保存结果的版本:

template<typename...Args>
struct Enforce;

template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> {
    static const bool value =
        std::is_constructible<T,T2>::value &&
        Enforce<T, Args...>::value;
};

template<typename T>
struct Enforce<T> : std::true_type {
};

现在,您可以在onlyInPairs

内移动断言更近
template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
    static_assert( Enforce<std::string, Arguments...>::value , "Wrong second arg..." );
}

答案 1 :(得分:1)

使用编译时递归:

void processArgPairs() {
    // to stop recursion
}

template <typename Arg1, typename Arg2, typename... Arguments>
void processArgPairs(Arg1 a, Arg2 b, Arguments&& ...args){
    static_assert(std::is_constructible<int, Arg1>::value, "Wrong type of first argument - int expected");
    static_assert(std::is_constructible<std::string, Arg2>::value, "Wrong type of second argument - string expected

    processArgPairs(std::forward<Arguments>(args)...);
}

template <typename... Arguments> unsigned int onlyInPairs
    (const std::string name, Arguments&& ... args)  {

    const unsigned numargs = sizeof...(Arguments);
    processArgPairs(std::forward<Arguments>(args)...);
    return numargs;
}

答案 2 :(得分:1)

你说的模板噪音是什么?

void onlyInPairs(std::initializer_list<std::pair<int, std::string>>&& pairs) {}

int main() {
    onlyInPairs({
        {1, "abc"},
        {2, "def"},
        {3, "foo"},
    });
}

答案 3 :(得分:0)

这样的东西?

template <typename... Arguments> 
unsigned int onlyInPairs(const std::string name, const Arguments& ... args)  
{

    const unsigned numargs = sizeof...(Arguments);
    check(args...);
    return numargs;
}
template <typename... Arguments>
void check(const int i, const std::string name, const Arguments& ... args)
{
    check(args...);
}
void check(const int i, const std::string name)
{

}

int main()
{
    auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works

    count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2); //compile error
    return 0;
}

答案 4 :(得分:0)

这是一个相当老派的解决方案:使用is_convertible应该更干净

#include <string>

template <typename... Args> struct EnforcePairsHelper;
// terminal case
template <> struct EnforcePairsHelper<> {
    enum { size = 0 };
};
// multiple specializations for reliable matching:
// only the last is really required here
template <typename... ArgTail>
struct EnforcePairsHelper<int, const char *, ArgTail...> {
    enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <typename... ArgTail>
struct EnforcePairsHelper<int, char *, ArgTail...> {
    enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <int N, typename... ArgTail>
struct EnforcePairsHelper<int, char [N], ArgTail...> {
    enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};

template <typename... Args> unsigned onlyInPairs (const std::string name,
                                                  const Args& ... args) {

    const unsigned numargs = EnforcePairsHelper<Args...>::size;

    // more magic would happen here with the parameters :)
    return numargs;
}

int main() {
    unsigned ok = onlyInPairs("ABC", 1, "DEF", 2, "HIJ");
    // unsigned no = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2);
}