Как избежать неявного приведения к неструктурирующим функциям?
У меня есть функция, которая принимает целое число в качестве параметра,
но эта функция также будет принимать символы, bools и longs.
Я полагаю, что это делает это, неявно бросая их.
Как можно избежать этого, чтобы функция принимала только параметры соответствующего типа и в противном случае отказалась бы компилировать?
Есть ключевое слово «явный», но оно не работает с неструктурирующими функциями. : \
что я делаю?
Следующая программа компилируется, хотя я бы этого не хотел:
#include <cstdlib>
//the function signature requires an int
void function(int i);
int main(){
int i{5};
function(i); //<- this is acceptable
char c{'a'};
function(c); //<- I would NOT like this to compile
return EXIT_SUCCESS;
}
void function(int i){return;}
* пожалуйста, обязательно укажите на любое неправильное использование терминологии и допущений
Вы не можете напрямую, потому что char
автоматически повышается до int
,
Вы можете прибегнуть к хитрости, хотя: создать функцию, которая принимает char
в качестве параметра и не реализуйте его. Он скомпилируется, но вы получите ошибку компоновщика:
void function(int i)
{
}
void function(char i);
//or, in C++11
void function(char i) = delete;
Вызов функции с char
параметр сломает сборку.
Увидеть http://ideone.com/2SRdM
Терминология: неструктурирующие функции? Вы имеете в виду функцию, которая не является конструктором?
Определите шаблонную функцию, которая соответствует всем другим типам:
void function(int); // this will be selected for int only
template <class T>
void function(T) = delete; // C++11
Это потому, что не шаблонные функции с прямым соответствием всегда рассматриваются в первую очередь. Затем рассматриваются шаблонные функции с прямым соответствием — поэтому никогда function<int>
будет использоваться. Но для всего остального, как чар, function<char>
будет использоваться — и это дает ваши ошибки компиляции:
void function(int) {}
template <class T>
void function(T) = delete; // C++11int main() {
function(1);
function(char(1)); // line 12
}
ОШИБКИ:
prog.cpp: In function 'int main()':
prog.cpp:4:6: error: deleted function 'void function(T) [with T = char]'
prog.cpp:12:20: error: used here
Это путь C ++ 03:
// because this ugly code will give you compilation error for all other types
class DeleteOverload
{
private:
DeleteOverload(void*);
};template <class T>
void function(T a, DeleteOverload = 0);
void function(int a)
{}
Вот общее решение, которое вызывает ошибку во время компиляции, если function
вызывается с чем угодно, кроме int
template <typename T>
struct is_int { static const bool value = false; };
template <>
struct is_int<int> { static const bool value = true; };template <typename T>
void function(T i) {
static_assert(is_int<T>::value, "argument is not int");
return;
}
int main() {
int i = 5;
char c = 'a';
function(i);
//function(c);
return 0;
}
Он работает, позволяя любому типу аргумента функционировать, но используя is_int
как предикат уровня типа. Общая реализация is_int
имеет ложное значение, но явная специализация для типа int имеет значение true, так что static assert гарантирует, что аргумент имеет точно тип int
в противном случае возникает ошибка компиляции.
Что ж, я собирался ответить на это с помощью приведенного ниже кода, но даже если он работает с Visual C ++, в смысле создания желаемой ошибки компиляции, MinGW g ++ 4.7.1 принимает ее и вызывает конструктор ссылок rvalue!
Я думаю, что это должно быть ошибка компилятора, но я могу ошибаться, так что — кто-нибудь?
Во всяком случае, вот код, который может оказывается стандартным решением (или может оказаться, что это с моей стороны Thinko!):
#include <iostream>
#include <utility> // std::is_same, std::enable_if
using namespace std;
template< class Type >
struct Boxed
{
Type value;
template< class Arg >
Boxed(
Arg const& v,
typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
)
: value( v )
{
wcout << "Generic!" << endl;
}
Boxed( Type&& v ): value( move( v ) )
{
wcout << "Rvalue!" << endl;
}
};
void function( Boxed< int > v ) {}
int main()
{
int i = 5;
function( i ); //<- this is acceptable
char c = 'a';
function( c ); //<- I would NOT like this to compile
}
Для C ++ 14 (и я полагаю, C ++ 11), вы можете отключить конструкторы копирования, также перегружая rvalue-ссылки:
Пример:
Скажем, у вас есть база Binding<C>
класс, где C
либо база Constraint
класс или унаследованный класс. Скажи, что вы храните Binding<C>
по значению в векторе, и вы передаете ссылку на привязку, и вы хотите убедиться, что вы не вызываете неявную копию.
Вы можете сделать это, удалив func(Binding<C>&& x)
(согласно примеру с PiotrNycz) для конкретных случаев с rvalue.
Snippet:
template<typename T>
void overload_info(const T& x) {
cout << "overload: " << "const " << name_trait<T>::name() << "&" << endl;
}
template<typename T>
void overload_info(T&& x) {
cout << "overload: " << name_trait<T>::name() << "&&" << endl;
}
template<typename T>
void disable_implicit_copy(T&& x) = delete;
template<typename T>
void disable_implicit_copy(const T& x) {
cout << "[valid] ";
overload_info<T>(x);
}
...
int main() {
Constraint c;
LinearConstraint lc(1);
Binding<Constraint> bc(&c, {});
Binding<LinearConstraint> blc(&lc, {});
CALL(overload_info<Binding<Constraint>>(bc));
CALL(overload_info<Binding<LinearConstraint>>(blc));
CALL(overload_info<Binding<Constraint>>(blc));
CALL(disable_implicit_copy<Binding<Constraint>>(bc));
// // Causes desired error
// CALL(disable_implicit_copy<Binding<Constraint>>(blc));
}
Выход:
>>> overload_info(bc)
overload: T&&
>>> overload_info<Binding<Constraint>>(bc)
overload: const Binding<Constraint>&
>>> overload_info<Binding<LinearConstraint>>(blc)
overload: const Binding<LinearConstraint>&
>>> overload_info<Binding<Constraint>>(blc)
implicit copy: Binding<LinearConstraint> -> Binding<Constraint>
overload: Binding<Constraint>&&
>>> disable_implicit_copy<Binding<Constraint>>(bc)
[valid] overload: const Binding<Constraint>&
Ошибка (с clang-3.9
в bazel
, когда оскорбительная строка не закомментирована):
cpp_quick/prevent_implicit_conversion.cc:116:8: error: call to deleted function 'disable_implicit_copy'
CALL(disable_implicit_copy<Binding<Constraint>>(blc));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Полный исходный код: prevent_implicit_conversion.cc
Может быть, вы можете использовать структуру, чтобы сделать вторую функцию приватной:
#include <cstdlib>
struct NoCast {
static void function(int i);
private:
static void function(char c);
};
int main(){
int i(5);
NoCast::function(i); //<- this is acceptable
char c('a');
NoCast::function(c); //<- Error
return EXIT_SUCCESS;
}
void NoCast::function(int i){return;}
Это не скомпилируется:
prog.cpp: In function ‘int main()’:
prog.cpp:7: error: ‘static void NoCast::function(char)’ is private
prog.cpp:16: error: within this context
Сначала я попробовал подход PiotrNycz (для C ++ 03, который я вынужден использовать для проекта), затем я попытался найти более общий подход и придумал это ForcedType<T>
шаблон класса.
template <typename T>
struct ForcedType {
ForcedType(T v): m_v(v) {}
operator T&() { return m_v; }
operator const T&() const { return m_v; }
private:
template <typename T2>
ForcedType(T2);
T m_v;
};
template <typename T>
struct ForcedType<const T&> {
ForcedType(const T& v): m_v(v) {}
operator const T&() const { return m_v; }
private:
template <typename T2>
ForcedType(const T2&);
const T& m_v;
};
template <typename T>
struct ForcedType<T&> {
ForcedType(T& v): m_v(v) {}
operator T&() { return m_v; }
operator const T&() const { return m_v; }
private:
template <typename T2>
ForcedType(T2&);
T& m_v;
};
Если я не ошибаюсь, эти три специализации должны охватывать все распространенные случаи использования. Я не уверен, действительно ли нужна специализация для rvalue-reference (на C ++ 11 и выше) или достаточно одного значения.
Можно использовать это следующим образом, в случае функции с 3 параметрами, чей 3-й параметр не допускает неявные преобразования:
function(ParamType1 param1, ParamType2 param2, ForcedType<ParamType3> param3);