Я знаю, что вы можете использовать ключевое слово C++ «явное» для конструкторов классов, чтобы предотвратить автоматическое преобразование типа. Можете ли вы использовать эту же команду, чтобы предотвратить преобразование параметров для метода класса?
У меня есть два члена класса, один из которых принимает в качестве параметра bool, а другой - беззнаковое int. Когда я вызвал функцию с int, компилятор преобразовал параметр в bool и вызвал неправильный метод. Я знаю, что в конце концов заменю bool, но пока не хочу нарушать работу других подпрограмм, поскольку эта новая подпрограмма разрабатывается.





Компилятор выдал предупреждение о "неоднозначном вызове", которого будет достаточно.
Я занимался разработкой TDD и не осознавал, что забыл реализовать соответствующий вызов в фиктивном объекте.
Возможно, вы захотите обновить вопрос, чтобы отразить, что указанная вами проблема (выбран неправильный метод) на самом деле не является проблемой. Я пересматривал это для своих собственных целей и понял, что даже мое решение не обязательно решает это, в зависимости от аргументов.
Вы также можете написать версию int, которая вызывает bool.
Он хочет предотвратить такое поведение.
Нет. explicit предотвращает автоматическое преобразование между определенными классами независимо от контекста. И, конечно, вы не можете этого сделать для встроенных классов.
Проголосовал; в конце концов, это действительно ответ на вопрос правильно.
bool ЯВЛЯЕТСЯ int, который ограничен значением 0 или 1. В этом вся концепция return 0; логически то же самое, что сказать return false; (однако не используйте это в коде).
Я считаю, что bool - это самостоятельный тип в C++.
Да, это тип, но этот тип может содержать только целые значения 0 и 1. На самом деле, если я помню своего инструктора по C++ из моих дней коллажа, 0 - это ложь, а все остальное - истина. Это внутренняя работа bool, а не то, как ее следует применять.
Не быть придурком, а «коллажными днями»? Хе-хе-хе ... заставил меня усмехнуться.
Вы правы, что bools могут содержать только значения 0 и 1; преобразовать bool в int, вы получите 0 или 1. «0 - ложь, а все остальное - истина» - применяется в обратном случае; если вы оцениваете int или другое числовое значение как bool. Идея возврата 0 фактически пришла из C, когда не было типа bool.
Я все еще должен быть в одном, мне потребовалось до сегодняшнего дня, чтобы уловить свой каламбур.
Нет, вы не можете использовать явное, но вы можете использовать шаблонную функцию для обнаружения неверных типов параметров.
С помощью C++ 11 вы можете объявить шаблонную функцию как deleted. Вот простой пример:
#include <iostream>
struct Thing {
void Foo(int value) {
std::cout << "Foo: value" << std::endl;
}
template <typename T>
void Foo(T value) = delete;
};
Это дает следующее сообщение об ошибке, если вы попытаетесь вызвать Thing::Foo с параметром size_t:
error: use of deleted function
‘void Thing::Foo(T) [with T = long unsigned int]’
В коде до C++ 11 это может быть выполнено с использованием неопределенной частной функции.
class ClassThatOnlyTakesBoolsAndUIntsAsArguments
{
public:
// Assume definitions for these exist elsewhere
void Method(bool arg1);
void Method(unsigned int arg1);
// Below just an example showing how to do the same thing with more arguments
void MethodWithMoreParms(bool arg1, SomeType& arg2);
void MethodWithMoreParms(unsigned int arg1, SomeType& arg2);
private:
// You can leave these undefined
template<typename T>
void Method(T arg1);
// Below just an example showing how to do the same thing with more arguments
template<typename T>
void MethodWithMoreParms(T arg1, SomeType& arg2);
};
Недостатком является то, что в этом случае код и сообщение об ошибке менее ясны, поэтому следует выбирать вариант C++ 11 всякий раз, когда он доступен.
Повторите этот шаблон для каждого метода, использующего bool или unsigned int. Не предоставляйте реализацию для шаблонной версии метода.
Это заставит пользователя всегда явно вызывать версию типа bool или unsigned int.
Любая попытка вызвать Method с типом, отличным от bool или unsigned int, не будет скомпилирована, потому что член является частным, конечно, с учетом стандартных исключений из правил видимости (друг, внутренние вызовы и т. д.). Если что-то, у кого есть доступ, вызывает частный метод, вы получите ошибку компоновщика.
Но это отключит ВСЕ автоматические преобразования.
Да, это отключит ВСЕ автоматические преобразования. Дэн хочет «предотвратить преобразование параметров для метода класса» согласно тексту вопроса, и этот метод удовлетворяет это. Есть способы использования специализации шаблонов, с помощью которых при желании мы могли бы «сопоставить» базовые типы с конкретными методами.
Я понимаю, что это старый ответ. Есть ли способ использовать delete или некоторые другие современные функции C++ для достижения того же самого без этого взлома private? Обновлено: Ахах! Оно работает!
Да, вы можете использовать delete, хотя я бы не назвал использование private хакерским. Просто не по-новому. Это то место, где StackOverflow не всегда справляется с изменениями. Как бы вы относились к включению своего ответа в мой, @Apollys, с разделами для C++ 11 и до C++ 11?
Конечно, вы бы предпочли оставить исходный ответ сверху или вариант C++ 11 сверху?
Сделанный. Сначала я разместил решение C++ 11, потому что в наши дни оно действительно должно стать популярным вариантом.
Что-то, что может сработать для вас, - это использовать шаблоны. Ниже показана функция шаблона foo<>(), специализированная для bool, unsigned int и int. Функция main() показывает, как разрешаются вызовы. Обратите внимание, что вызовы, использующие константу int без суффикса типа, будут разрешены в foo<int>(), поэтому вы получите сообщение об ошибке при вызове foo( 1), если вы не специализируетесь на int. В этом случае вызывающие абоненты, использующие буквальную целочисленную константу, должны будут использовать суффикс "U" для разрешения вызова (это может быть желаемое поведение).
В противном случае вам придется специализироваться на int и использовать суффикс "U" или преобразовать его в unsigned int, прежде чем передавать его в версию unsigned int (или, возможно, сделать утверждение, что значение не отрицательное, если вы этого хотите).
#include <stdio.h>
template <typename T>
void foo( T);
template <>
void foo<bool>( bool x)
{
printf( "foo( bool)\n");
}
template <>
void foo<unsigned int>( unsigned int x)
{
printf( "foo( unsigned int)\n");
}
template <>
void foo<int>( int x)
{
printf( "foo( int)\n");
}
int main ()
{
foo( true);
foo( false);
foo( static_cast<unsigned int>( 0));
foo( 0U);
foo( 1U);
foo( 2U);
foo( 0);
foo( 1);
foo( 2);
}
Я тоже думал о том, чтобы сделать это таким образом; если foo <int> вызывает foo <unsigned int> для явного сопоставления. Это лучший способ, если он хочет разрешить некоторые преобразования, но не все.
Собственно, это действительно тот же метод, что и ваш. Когда я быстро просканировал ваш незадолго до публикации, я был сбит с толку многопараметрическими прототипами и подумал, что ваша техника делает что-то другое, чего я не полностью понимал. Надо было читать повнимательнее.
Ах, многопараметрические версии должны были прояснить, что это применимо не только к методам с одним параметром, и что только рассматриваемый аргумент должен быть шаблонизирован. Может быть, они больше сбивали с толку, чем приносили пользу? Я добавлю комментарий, чтобы прояснить это.
То, что они немного сбили меня с толку, не означает, что они сбивали с толку. Если в этом есть смысл.
Ниже приводится очень простая оболочка, которую можно использовать для создания строгого определения типа:
template <typename V, class D>
class StrongType
{
public:
inline explicit StrongType(V const &v)
: m_v(v)
{}
inline operator V () const
{
return m_v;
}
private:
V m_v; // use V as "inner" type
};
class Tag1;
typedef StrongType<int, Tag1> Tag1Type;
void b1 (Tag1Type);
void b2 (int i)
{
b1 (Tag1Type (i));
b1 (i); // Error
}
Одной из приятных особенностей этого подхода является то, что вы также можете различать разные параметры одного и того же типа. Например, у вас может быть следующее:
class WidthTag;
typedef StrongType<int, WidthTag> Width;
class HeightTag;
typedef StrongType<int, HeightTag> Height;
void foo (Width width, Height height);
Клиентам foo будет ясно, какой аргумент является каким.
Хотя вопрос довольно старый (размышлял 9 лет): каждый раз, когда я хочу вызвать foo с двумя целочисленными параметрами, я должен написать foo( Height ( int_1 ), Width ( int_2 ) ), который найдет оператор явного преобразования через Argument Dependent Lookup / Koenig's Lookup?
@SebTu: № Height и Width должны быть найдены без поиска ADL. Выражение Height(int_1) - это явное приведение типа, в результате которого вызывается конструктор Height. ADL здесь не применяется. Следовательно, чтобы обратиться к NS::Height или NS::Width, мы должны либо использовать директиву / объявление using, либо явно указать имена.
В настоящее время принятый отвечать (с использованием частной шаблонной функции) хорош, но устарел. В C++ 11 вместо этого мы можем использовать функции deleted:
#include <iostream>
struct Thing {
void Foo(int value) {
std::cout << "Foo: value" << std::endl;
}
template <typename T>
void Foo(T value) = delete;
};
int main() {
Thing t;
int int_value = 1;
size_t size_t_value = 2;
t.Foo(int_value);
// t.Foo(size_t_value); // fails with below error
// error: use of deleted function
// ‘void Thing::Foo(T) [with T = long unsigned int]’
return 0;
}
Это более точно передает намерение исходного кода и предоставляет пользователю более четкое сообщение об ошибке при попытке использовать функцию с запрещенными типами параметров.
Мне нравится содержание этого, но в нем можно было бы использовать некоторые уточняющие детали и уточнения. Например, требуется поддержка компилятора C++ 11 и постоянная ссылка на принятый ответ (поскольку принятый ответ может быть изменен!).
Интересно то же самое, и думаю, что это будет полезный синтаксис для некоторых бесплатных функций. Обычно я хочу, чтобы ссылка на производный класс неявно давала базовый класс, за исключением случаев, когда может произойти нежелательное сечение, например, с функцией swap (). Наличие подкачки (явное Foo & lhs, явное Foo & rhs) было бы утешительным.