有没有办法消除这种歧义?

Is there a way to force this ambiguity away?

我想提供两种形式的 GetLength(psz) 样式函数 - 一种不知道上限,另一种知道:

template <typename T>
size_t GetLength(const T * psz) { /* compute size w/o knowing what upper bound may be */ }

template <typename T, size_t size>
size_t GetLength(const T(&psz)[size]) { /* we know the upper bound */ }

我希望这不是模棱两可的。当参数是已知大小的数组时,我希望选择数组大小的版本。当参数只是一个指针而不是已知的固定数组时,我希望选择无界版本。

我还提供了第三个版本,它明确地将上限作为参数,没有模板化大小扣除,用于从外部上下文传递该信息,否则该外部上下文将失去从其本地参数推断出该信息的能力.

有没有一种技术可以用来强制编译器在边界已知时打折函数的第一个版本(未知边界)?

Is there a technique I can use to force the compiler to discount the 1st version of my function (no known bounds) when the bounds is known?

添加一个间接级别怎么样?

template <typename T>
std::size_t GetLength (const T * psz, int)
 { /* compute size w/o knowing what upper bound may be */ }

template <typename T, size_t size>
std::size_t GetLength (const T(&psz)[size], long)
 { /* we know the upper bound */ }

template <typename T>
std::size_t GetLength (T const & t)
 { GetLength(t, 0L); }

添加一个未使用的不同参数(intlong)您可以 select 首选版本。

我们可以使用类型特征:

#include <type_traits>

   // If T is an array 
   template<
       typename T,
       typename std::enable_if <
       std::is_array<T>{},
       size_t
       > ::type Extent = std::extent<T>::value
   >
   size_t GetLength(const T& t)
   {
       return Extent;
   }

   // If T is not an array 
   template<typename T,
       typename std::enable_if <
       !std::is_array<T>{},
       size_t
       > ::type = 0
   >
   size_t GetLength(const T& t)
   {
       return {};
   }

   int main()
   {
       int arr[5]{};
       GetLength(arr); // calls first

       //decay to pointer
       auto val = arr;
       GetLength(val); // calls second
   }

如果您可以访问最新版本的 boost,则可以使用非常强大的 HOF 库(代表高阶函数)。

我最常用来简化基于参数类型的代码路径选择的函数之一是函数 first_of.

它的工作方式是按照您希望编译器尝试的顺序给它一个模板函数对象(或 lambda)列表。列表中的第一个合法函数对象被选中。

示例:

#include <cstddef>
#include <boost/hof.hpp>
#include <cstring>
#include <utility>
#include <iostream>

// a function to compute length from a pointer. For exposition, 
// I have only considered char pointers but any number of overloads will work.
template<class T> 
std::size_t 
string_pointer_length(T*p)
{
    // for exposition
    return std::strlen(p);
}

// a function to compute string length from a literal
template<class T, std::size_t N> 
constexpr 
std::size_t literal_string_length(T (&s)[N])
{
    return N - 1;
}

// The generic GetLength function which takes any kind of string
template <typename T>
std::size_t GetLength(T&& str) 
{ 
    // select the FIRST legal choice of the following lambdas and invoke...
    return boost::hof::first_of(
        [](auto&&s) BOOST_HOF_RETURNS(literal_string_length(s)),
        [](auto&&s) BOOST_HOF_RETURNS(string_pointer_length(s))
    )(str);
}


int main()
{
    static const auto lit = "hello";
    auto plit = std::addressof(lit[0]);

    auto n = GetLength(lit);
    auto n2 = GetLength(plit);

    std::cout << n << ", " << n2 << std::endl;
}

BOOST_HOF_RETURNS 让我们不必像这样拼写 lambda:

    return boost::hof::first_of(
        [](auto&&s) -> decltype(literal_string_length(s)) { return literal_string_length(s); },
        [](auto&&s) BOOST_HOF_RETURNS(string_pointer_length(s))
    )(str);

如果您不能使用 boost.hof,编写我们自己的替代品非常简单:

#include <cstddef>
#include <cstring>
#include <tuple>
#include <utility>
#include <iostream>

template<class T> 
std::size_t 
string_pointer_length(T*p)
{
    // for exposition
    return std::strlen(p);
}

template<class T, std::size_t N> 
constexpr 
std::size_t literal_string_length(T (&s)[N])
{
    return N - 1;
}

template<class...Args, class This, class...Others>
constexpr auto try_these(std::tuple<Args...> args, This _this, Others...others)
{
    if constexpr (std::is_invocable_v<This, Args...>)
    {
        return std::apply(_this, args);
    }
    else
    {
        return try_these(args, others...);
    }
}

struct invoke_string_pointer_length
{
    template<class S>
    constexpr auto operator()(S&& s) const -> decltype(string_pointer_length(s)) 
    { return string_pointer_length(s); }
};

struct invoke_literal_string_length
{
    template<class S>
    constexpr auto operator()(S&& s) const -> decltype(literal_string_length(s)) 
    { return literal_string_length(s); }
};

template <typename T>
std::size_t GetLength(T&& str) 
{ 
    return try_these(std::forward_as_tuple(std::forward<T>(str)), 
        invoke_literal_string_length(), 
        invoke_string_pointer_length());
}


int main()
{
    static const auto lit = "hello";
    auto plit = std::addressof(lit[0]);

    auto n = GetLength(lit);
    auto n2 = GetLength(plit);

    std::cout << n << ", " << n2 << std::endl;
}