如何限制参数类型仅允许 std::initializer_list<size_t> 或 std::array<size_t, N>?

5

我希望只有一个模板函数。因此,我想到了如下方法...

template<typename Iteratable, size_t N,
         typename = 
            std::enable_if_t<
                std::is_same_v<Iteratable, const std::initializer_list<size_t> > ||
                std::is_same_v<Iteratable, const std::array<size_t, N > >
            > 
>
std::ostream& operator << (std::ostream& os, Iteratable& in) {
    std::copy(std::begin(in), std::end(in), std::ostream_iterator<size_t>(os, " ") ); 
    return os;
}

看起来是因为在std::array<size_t,N>中的N,导致了特化失败。

有没有什么方法可以不写两个函数来处理这种情况?


两个函数只需要9行代码,现在你花了11行代码来避免重复? :-) 但是你关于N不能从initializer_list中推导出来是正确的。 - Bo Persson
1个回答

8

如果你不想使用函数体重载的唯一原因是为了避免重复,那么你可以考虑编写自己的 trait。以下是一种方式:

namespace details {
  template<class Iterable>
  struct writable : std::false_type {};

  template<size_t N>
  struct writable<std::array<std::size_t, N>> : std::true_type {};

  template<>
  struct writable<std::initializer_list<size_t>> : std::true_type {};

  template<class Iterable>
  constexpr bool writable_v = writable<Iterable>::value;
}

template<typename Iteratable,
         std::enable_if_t<details::writable_v<std::decay_t<Iteratable>>,
         int> = 0 
>
std::ostream& operator << (std::ostream& os, Iteratable& in) {
    std::copy(std::begin(in), std::end(in), std::ostream_iterator<size_t>(os, " ") ); 
    return os;
}

我也顺便把 enable_if 移到了模板参数中。这样,通过为两个参数指定相同的类型名称来规避 SFINAE 就不可能了(尽管承认,在重载操作符中发生这种情况的可能性很小)。
另一个有趣的事实是,自定义点现在与函数定义本身分离开来了。添加新的可迭代对象只需要为 details::writable 添加另一个特化即可。 实时示例

这表达了我的意图。顺便说一下,我是一个sfinae的新手,你能否解释一下关于typename = enable_ifenable_if = 0的enable_if惯用语? - sandthorn
1
@sandthorn - 有9次中的10次它们都具有相同的目的。然而,还有一些注意事项。(1) 默认参数不能区分两个本质相同的重载。如果您尝试对互斥的构造函数集合进行SFINAE,则会遇到此问题。(2) 对于可能明确指定参数的情况,类似my_func<std::vector<std::size_t>, bool>的东西可以完全绕过SFINAE检查。 - StoryTeller - Unslander Monica
这样怎么样:enable_if_t< ,std::errc> = std::errc::operation_not_supported :使用强类型枚举类是否有助于提高SFINAE性并同时表达意图? - sandthorn
1
@sandthorn - 是的,枚举是一个有效的非类型模板参数,所以这将起作用。但不知道在实践中有多常见。您必须记住,SFINAE通常用于从一组重载函数中取消资格。这意味着不会有错误可供查看,但会选择另一个重载。 - StoryTeller - Unslander Monica

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