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

时间:2019-05-08 21:22:57

标签: c++ arrays templates overload-resolution

我想提供两种形式的re样式函数-一种不知道上限,另一种可以做到:

GetLength(psz)

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

我还将提供第三个版本,该版本显式地将上限作为参数,而没有模板大小的缩减,用于从外部上下文中传递该信息,否则将无法从其本地参数中推断出该信息。 / p>

有什么技术可以强制编译器在知道边界的情况下取消函数的第一个版本(没有已知边界)?

3 个答案:

答案 0 :(得分:2)

  

有什么技术可以强制编译器在知道边界的情况下取消函数的第一个版本(没有已知边界)?

添加间接级别怎么样?

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),可以选择首选版本。

答案 1 :(得分:1)

我们可以使用类型特征:

#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
   }

答案 2 :(得分:1)

如果您可以访问最新版本的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;
}