forward_list: assign (_InputIterator __first, _InputIterator __last) / assign (size_type __n, const _Tp & amp; __val)

Я реализовал подмножество forward_list и хотел проверить метод assign(size_type __n, const _Tp& __val) но я получаю ошибку компилятора, потому что компилятор хочет вызвать метод assign(_InputIterator __first, _InputIterator __last) вместо.

Я написал следующий фрагмент, чтобы проиллюстрировать проблему:

test.h

#ifndef TEST_H
#define TEST_H

#include <utility> // Just to get the std::size_t

template<typename _Tp>
class forward_list {
public:
typedef std::size_t size_type;

void assign(size_type n, const _Tp& val)
{
printf("%s\n", __PRETTY_FUNCTION__);
}

template<typename _InputIterator>
void assign(_InputIterator first, _InputIterator last)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
};

#endif // TEST_H

test.cpp

#include <stdlib.h>
#include <stdio.h>
#include "test.h"
int main()
{
forward_list<int> l;
l.assign(10, 5);
return 0;
}

Результат выполнения:

void forward_list<_Tp>::assign(_InputIterator, _InputIterator) [with _InputIterator = int; _Tp = int]

Я хотел бы иметь метод assign(size_type __n, const _Tp& __val) называется.

Версия компилятора (на всякий случай): g++ (Debian 4.7.2-5) 4.7.2

Я использовал аналогичные подписи к подписям, используемым в std::forward_list и со следующим фрагментом кода (используя STL):

std::forward_list<int> l;
l.assign(10, 5);

Компилятор знает, что он должен вызывать assign(size_type __n, const _Tp& __val) и не запутаться. Что мне не хватает?

1

Решение

Когда вы звоните l.assign(10, 5);Есть две жизнеспособные перегрузки:

void assign(size_type n, const int& val)

template <>
void assign(int first, int last)

Когда мы говорим, что не шаблонные функции предпочтительнее шаблонных функций, это верно только в том случае, если они имеют неразличимые последовательности преобразования. Но в этом случае шаблон функции будет точно соответствовать (оба ваших аргумента int, нет необходимости в конвертации), в то время как не шаблон должен проходить промоушен (продвигать 10 от int в size_t). Вот почему перегрузка шаблона функции предпочтительнее.

Что касается того, как это исправить, вам просто нужно сделать шаблон не жизнеспособная перегрузка. Это включает в себя написание type_trait для входного итератора, который использует void_t не сложно

template <typename... >
using void_t = void;

template <typename T, typename = void>
struct is_input_iterator : std::false_type { };

template <typename T>
struct is_input_iterator<T, void_t<
decltype(std::declval<T>() == std::declval<T>()),
decltype(std::declval<T>() != std::declval<T>()),
decltype(*std::declval<T>()),
decltype(++std::declval<T>()),
decltype(std::declval<T>()++)
>> : std::true_type { };

А потом требуют is_input_iterator:

template <typename _InputIterator,
typename = std::enable_if_t<is_input_iterator<_InputIterator>::value>>
void assign(_InputIterator first, _InputIterator last);

Есть много других способов сделать это, мне просто нравится void_t, Независимо от того, каким образом вы это делаете, вы должны убедиться, что шаблон просто не жизнеспособен.

1

Другие решения

assign перегрузка, которую вы хотите вызвать, принимает unsigned целочисленный тип в качестве первого аргумента, но вы передаете ему два целых числа со знаком. Если вы измените вызов на

l.assign(10U, 5);  // make the first argument unsigned

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

Вы должны ограничить assign шаблон, так что он жизнеспособен только тогда, когда тип аргументов удовлетворяет требованиям для итераторов. Один из способов сделать это — проверить iterator_traits для вовлеченного типа. Если iterator_category удовлетворяет требованиям InputIterator, тогда можно использовать шаблон функции.

template<typename InputIterator>
typename std::enable_if<
std::is_base_of<std::input_iterator_tag,
typename std::iterator_traits<InputIterator>::iterator_category
>::value
>::type
assign(InputIterator first, InputIterator last)
{
printf("%s\n", __PRETTY_FUNCTION__);
}

Живая демо

Обратите внимание, что технически вышеупомянутое решение не гарантированно работает до C ++ 17 (или как бы оно ни называлось), потому что iterator_traits не требуется быть SFINAE дружественным до тех пор, и это может привести к серьезной ошибке вместо ошибки замены. Но шансы вашей реализации iterator_traits уже СФИНАЕ дружественный, и вы не столкнетесь ни с какими проблемами.


size_t не гарантировано быть включенным <utility>, используйте один из заголовков, перечисленных на связанной странице.

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

1

По вопросам рекламы [email protected]