Variadic шаблонный метод специализации - PullRequest
1 голос
/ 04 июня 2019

Это код, который у меня сейчас есть:

class Foo
{
public:
    template<typename T, typename... Args>
    void Function(T t1, Args... args){
        // Definition
    }

private:
    template<typename T>
    void Function(T t1){
        // Definition
    }
};

#include "header.h"

int main()
{
    Foo foo;
    foo.Function(1, 2, 3, 4, 5);
    return 0;
}

Работает просто отлично.Когда я пытаюсь отделить определение от source.cpp, gcc начинает жаловаться.Я знаю, что я должен специализировать шаблоны, чтобы отделить определение, поэтому я попытался добавить приведенный ниже код в заголовочный файл:

template<>
void Foo::Function<int, int...>(int t1, int... args);

template<>
void Foo::Function<int>(int);

, но безуспешно.Чего мне не хватает


изменить: сообщения об ошибках gcc:

header.h: 15: 28: ошибка: шаблон расширения 'int' не содержит пакетов аргументов void Foo:: Функция (int t1, int ... args);

header.h: 15: 48: ошибка: шаблон расширения 'int' не содержит пакетов аргументов void Foo :: Function (int t1, int ... args);

Ответы [ 2 ]

2 голосов
/ 04 июня 2019

Вы не можете использовать int... в качестве пакета параметров, и поэтому это не работает. Кроме того, чтобы отделить источник от определения, вы должны полностью указать шаблон, поэтому int... не будет работать, даже если этот синтаксис был разрешен.

Способы обойти это.

1. Заставьте Function принять список инициализаторов. Мы можем написать функцию так, чтобы она принимала список инициализаторов int s:

#include <initializer_list>

class Foo {
   public:
    void Function(int t1, std::initializer_list<int> t2);
};

void Foo::Function(int t1, std::initializer_list<int> t2) {
    for(int i : t2) {
        // stuff
    }
}

Теперь вы можете довольно просто позвонить на Function, и он даже не настроен:

Foo f; 
f.Function(10, {1, 2, 3, 4, 5});

Если есть другие места, где вы используете шаблоны, вы можете развернуть пакет параметров непосредственно в списке инициализатора:

template<class... Args>
void invoke_foo(Foo& f, int first, Args... rest) {
    f.Function(first, {rest...}); 
}

2. Используйте SFINAE для отключения всех перегрузок не-int. Мы можем отключить все перегрузки Foo::Function, которые не только принимают int s

#include <type_traits>

class Foo {
   public:
    // Requires C++17 for std::conjunction
    // You could write your own std::conjunction too if you'd prefer
    template<class... Args>
    auto Function(int t1, Args... t2)
        -> std::enable_if_t<std::conjunction<std::is_same<Args, int>...>::value>
    {
        // stuff
    }
}; 

Недостатком этого является то, что нецелые значения не будут автоматически преобразованы в int.

0 голосов
/ 05 июня 2019

Есть лучший способ сделать это. Во-первых, похоже, что вы хотите использовать один и тот же тип всех аргументов (это делается с помощью std::initializer_list в принятом ответе). Это можно предвидеть, предоставив дополнительный явный аргумент:

class Foo
{
public:
    template<typename T, typename... Args>
    void Function(T t1, T t2, Args... args)
    {
        LOG;
        this->Function(t1);
        this->Function(t2, args...);
    }

private:
    template<typename T>
    void Function(T t1)
    {
        LOG << VAR(t1);
    }
};

template<>
void Foo::Function<int>(int x)
{
    LOG << " Spec" << VAR(x);
}

Как видите, достаточно указать специализацию метода для одного аргумента.

Живая демоверсия

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...