Mustang
Mustang

Reputation: 417

Battling type parameter order and enable_if specifications in function templates

Consider:

template <typename InputIt, typename T = typename std::remove_const<typename InputIt::value_type>::type>
  std::vector<T> WorkOnIt( InputIt first, InputIt last)
{
    std::vector<T> result(first, last);
    ...fiddle with result...
    return result;
}
...
std::set<OneType> goods;
std::vector<OneType> cooked;
...
cooked = WorkOnIt( goods.begin(), goods.end());

This compiles and works but I'm not happy with it even though the call site is clean. If I want to explicitly specify T, I can't just WorkOnIt<CookedType> because I'd have to specify InputIt in front of that. That's ugly since that type is manifest in the argument list. And besides I just want the input iterators to cough up something that is convertible to T. Say like this:

std::set<SourceType> goods;
std::vector<CookedType> cooked;
...
cooked = WorkOnIt<CookedType>( goods.begin(), goods.end());

There must be some enable_if incantation that does what I want but I can't figure it out. (BTW, I'm stuck on C++17.)

Upvotes: 1

Views: 148

Answers (1)

user7860670
user7860670

Reputation: 37578

You can try using two overloads: one with deduced Item parameter and another with explicit.

#include <set>
#include <string>
#include <type_traits>
#include <vector>
#include <iostream>

template <typename Item, typename InputIt>
[[nodiscard]] ::std::vector<Item> WorkOnIt(InputIt first, InputIt last)
{
    ::std::cout << "explicit Item\n";
    ::std::vector<Item> result{first, last};
    //...fiddle with result...
    return result;
}

template <typename InputIt, typename Item = ::std::remove_const_t<typename InputIt::value_type>>
[[nodiscard]] ::std::vector<Item> WorkOnIt(InputIt first, InputIt last)
{
    ::std::cout << "implicit Item\n";
    return ::WorkOnIt<Item, InputIt>(first, last);
}

int main()
{
    ::std::set<::std::string> goods{};
    ::std::vector<::std::string> cooked{};
    cooked = ::WorkOnIt<::std::string>(goods.begin(), goods.end());
    cooked = ::WorkOnIt(goods.begin(), goods.end());
    return 0;
}

online compiler

explicit Item
implicit Item
explicit Item

If forward declaration of this function is not required then code can be simplified to utilize deduced return type and to eliminate deduced Item parameter completely:

#include <set>
#include <string>
#include <type_traits>
#include <vector>
#include <iostream>

template <typename Item, typename InputIt>
[[nodiscard]] auto WorkOnIt(InputIt first, InputIt last)
{
    ::std::cout << "explicit Item\n";
    ::std::vector<Item> result{first, last};
    //...fiddle with result...
    return result;
}

template <typename InputIt>
[[nodiscard]] auto WorkOnIt(InputIt first, InputIt last)
{
    ::std::cout << "implicit Item\n";
    return ::WorkOnIt<::std::remove_const_t<typename InputIt::value_type>, InputIt>(first, last);
}

int main()
{
    ::std::set<::std::string> goods{};
    ::std::vector<::std::string> cooked{};
    cooked = ::WorkOnIt<::std::string>(goods.begin(), goods.end());
    cooked = ::WorkOnIt(goods.begin(), goods.end());
    return 0;
}

online compiler

Upvotes: 1

Related Questions