如何防止C ++猜测第二个模板参数?


26

我正在使用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
我猜想传递一个自定义的迭代器,该迭代器将a包裹起来,char*但不是一个不是解决方案吗?
康拉德·鲁道夫

1
@KonradRudolph:这是一种解决方法,但是没有回答我的问题。实际上,我已经有了另一种解决方法(特定于/*...*/中的内容),但是我想在这里走高路。
einpoklum

1
不幸的是,在这种情况下,我的猜测是“无法完成”。公平地说,我不确定我是否会在自己的代码中接受建议的解决方法。
康拉德·鲁道夫

只是为了澄清一下:您是否想要一个通用的解决方案,该解决方案始终可以用来区分具有一个参数还是两个参数的模板重载之间的调用,还是只需要一种针对这种情况的解决方案?
核桃

@walnut:一般解决方案会更好;我的具体情况主要是问题的动机。
einpoklum

Answers:


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::range

range1_ptr<T>(...) 总是可以使用一个模板参数来显式调用模板,但不会从参数中进行任何推导。 range1复制原始strf::range模板中的推论。

这行得通,因为 可行的 [temp.deduct.funcaddr] / 1表示,在不带转换目标类型的函数的地址处进行模板参数推导时,在每个候选函数模板上都进行了推定,就好像假设调用的参数和参数列表是空的。因此,无法通过两个模板参数推导第二次重载的第二个模板参数。剩下的唯一候选对象是第一个重载,它将被选作函数指针的目标。

只要没有第二个候选函数模板可以形成仅带有一个自变量的有效模板ID,range1_ptr就可以始终使用它来明确地调用带有一个自变量的函数模板。否则,range1_ptr由于不明确,实例化将产生错误。


不会有歧义strf::range<T>吗?
einpoklum

1
@einpoklum在GCC和Clang上可以正常编译。我没有检查标准,但是如果认为标准不明确,我会感到惊讶。
核桃

也许您应该将函数名称更改为pretty_please_with_sugar_on_top()?...有时C ++可能很奇怪...
einpoklum

您设法
取消

11

通过a 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 *>;);这样,我想您有一个通用的解决方案,几乎等同于核桃的解决方案
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 *)左侧或右侧替换为,但仍可以使用。


如果参数指向const数据,这将非常丑陋。
aschepler

1. 不是第二个参数。我想要单参数模板。2.有趣的hack!-1为第一个建议,+ 1为第二个建议:-P
einpoklum
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.