如何避免C++猜测第二个模板参数?

27

我正在使用一个C++库(strf),这个库的某个位置有以下代码:

namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }

template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}

现在,我想在我的代码中使用strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)。但是如果我这样做,我会遇到以下错误(使用CUDA 10.1的NVCC):

error: more than one instance of overloaded function "strf::range" matches the argument list:
            function template "auto strf::range(ForwardIt, ForwardIt)"
            function template "auto strf::range(const Range &, const CharT *)"
            argument types are: (util::constexpr_string::const_iterator, util::constexpr_string::const_iterator)

这个的代码可能可以更改以避免这种情况(例如使用:

inline auto range(const typename std::enable_if<not std::is_pointer<typename std::remove_cv<Range>::type>::value, Range &>::type range, const CharT* sep)

我想确保 Range 不是一个指针,但目前不能修改它。相反,我想以某种方式表明给编译器看我只想只有一个模板参数,而不是一个已经指定和另一个被推断的。

我可以这样做吗?

希望提供C++11和C++14的答案;对于涉及推导指南的C++17答案可能不太相关,但如果您有,请发布它(为将来的NVCC版本...)

更新: strf库本身已进行了更新,以规避此情况,但问题仍然存在。


1
我猜传递一个自定义迭代器,它薄薄地包装了一个 char* 但实际上并不是 char* 的解决方案不可行? - Konrad Rudolph
1
@KonradRudolph:那是一个解决方法,但并没有回答我的问题。实际上我已经有另一种解决方法(特定于/.../中的内容),但我想采取更高明的方法。 - einpoklum
1
在这种情况下,我的(猜测的)答案是“无法完成”,不幸的是。公平地说,我不确定我会在自己的代码中接受我提出的解决方法。 - Konrad Rudolph
只是想澄清一下:您是想要一个通用的解决方案,总是能够区分具有一个参数和两个参数的模板重载之间的调用,还是只需要针对这种情况的特定解决方案? - walnut
@einpoklum 可能这是有意设计的,即你尝试使用 C++ 的方式并不是预期的。特别地,使用指针来表示迭代器对我来说听起来像是个糟糕的想法 :). - Chenna V
显示剩余7条评论
3个回答

16
template<typename T>
inline constexpr auto range1_ptr = strf::range<T>;

template<typename T>
inline decltype(auto) range1(T begin, T end) {
    return range1_ptr<T>(begin, end);
}

然后调用range1而不是strf::rangerange1_ptr<T>(...)总是可以用于显式地调用只带一个模板参数的模板,但不会从参数中进行任何推导。 range1 复制了原始strf::range模板的推导。
这样做是可行的,因为[temp.deduct.funcaddr]/1说,在没有转换目标类型的情况下获取函数地址时,对每个候选函数模板执行模板参数推导,就好像虚拟调用的参数和参数列表为空一样。 因此,第二个重载的第二个模板参数无法被推导。唯一剩下的候选者是第一个重载,它将被选择作为函数指针的目标。
只要不存在第二个候选函数模板,其中可以形成仅具有一个参数的有效模板-id,range1_ptr 就始终可以用于明确调用只带一个参数的函数模板。否则,由于歧义,range1_ptr 的实例化将产生错误。

strf::range<T> 会不会存在歧义? - einpoklum
1
@einpoklum 在GCC和Clang上编译正常。我没有检查标准,但如果那应该是模棱两可的话,我会感到惊讶的。 - walnut
也许你应该把函数名改成 pretty_please_with_sugar_on_top()?... C++ 有时候真的很奇怪... - einpoklum

11

那么通过 using 呢?

using tfp = void(*)(char const *, char const *);

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);

这个能编译通过吗?第二行看起来特别可疑。 - einpoklum
@einpoklum - 挺有趣的,不是吗? - max66
@einpoklum - 不幸的是这不是一个通用解决方案;在这种情况下起作用是因为(如果我没记错的话)只有第一个 range() 版本与 tpf 兼容;其他情况可能会有所不同。 - max66
@einpoklum - 在第二行中你还可以详细说明模板参数(tfp x = &strf::range<char const*>;);这样做我想你就有了一个通用的解决方案,几乎等价于walnut的方案。 - max66

0
一个解决方案是:
1)首先,您应该为第二个参数指定类型,例如(char *)(some_char_ptr + some_length) 2)不要为两者使用const,这样可以正常工作:
strf::range((char *)some_char_ptr, (char *)(some_char_ptr + some_length));

你可以尝试将左边或右边的(char *)替换为(const char *),它仍然可以工作。

  1. 没有第二个参数。我想要单参数模板。
  2. 有趣的黑客!对于第一个建议给-1分,对于第二个建议给+1分 :-P
- einpoklum

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