Мотивация:
Почти ради интереса я пытаюсь написать перегрузку функции, которая может отдельно отличить, является ли аргумент массивом фиксированного размера или указателем.
double const d[] = {1.,2.,3.};
double a;
double const* p = &a;
f(d); // call array version
f(p); // call pointer version
Я нахожу это особенно трудным из-за хорошо известного факта, что массивы распадаются на указатель раньше, чем позже. Наивным подходом было бы написать
void f(double const* c){...}
template<size_t N> void f(double const(&a)[N]){...}
К сожалению, это не работает. Потому что в лучшем случае компилятор определяет вызов массива f(d)
выше как неоднозначный.
Частичное решение:
Я перепробовал много вещей, и ближе всего я мог получить следующий конкретный код. Также обратите внимание, что в этом примере кода я использую char
вместо double
, но в конце он очень похож.
Во-первых, я должен использовать SFINAE, чтобы отключить преобразования (из массива ref в ptr) в версии указателя функции. Во-вторых, мне пришлось перегрузить все возможные размеры массивов (вручную).
[компилируемый код]
#include<type_traits> // for enable_if (use boost enable_if in C++98)
#include<iostream>
template<class Char, typename = typename std::enable_if<std::is_same<Char, char>::value>::type>
void f(Char const* dptr){std::cout << "ptr" << std::endl;} // preferred it seems
void f(char const (&darr)[0] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[1] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[2] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[3] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[4] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[5] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[6] ){std::cout << "const arr" << std::endl;} // this is the one called in this particular example
// ad infinitum ...
int main(){
f("hello"); // print ptr, ok because this is the fixed size array
f(std::string("hello").c_str()); // print arr, ok because 'c_str()' is a pointer
}
Это работает, но проблема в том, что мне нужно повторить функцию для всех возможных значений N
, и использование template<size_t N>
возвращает меня к нулю, потому что с параметром шаблона два вызова возвращаются в равную основу, В других работах template<size_t N> void f(char const(&a)[N]){std::cout << "const arr" << std::endl;}
не помогает.
Есть ли способ обобщить вторую перегрузку, не возвращаясь к неоднозначному вызову? или есть какой-то другой подход?
Ответ C++ или C++ 1XYZ также приветствуется.
Две детали: 1) я использовал clang
для экспериментов выше, 2) фактический f
в итоге станет operator<<
, я думаю, знаю, будет ли это иметь значение для решения.
Краткое изложение решений (основано на других людях ниже) и адаптировано к конкретному типу char
примера. Похоже, что оба они делают указатель char const*
менее очевидным для компилятора:
1) Один странный (переносимый?) (Из комментария @dyp.) Добавление ссылочного квалификатора к версии указателя:
template<class Char, typename = typename std::enable_if<std::is_same<Char, char>::value>::type>
void f(Char const* const& dptr){std::cout << "ptr" << std::endl;}
template<size_t N>
void f(char const (&darr)[N] ){std::cout << "const arr" << std::endl;}
2) Один элегантный (особый случай от @user657267)
template<class CharConstPtr, typename = typename std::enable_if<std::is_same<CharConstPtr, char const*>::value>::type>
void f(CharConstPtr dptr){std::cout << "ptr" << std::endl;}
template<size_t N>
void f(char const (&darr)[N] ){std::cout << "const arr" << std::endl;}