Можно ли использовать явное ключевое слово, чтобы предотвратить автоматическое преобразование параметров метода?

Я знаю, что вы можете использовать ключевое слово C++ «явное» для конструкторов классов, чтобы предотвратить автоматическое преобразование типа. Можете ли вы использовать эту же команду, чтобы предотвратить преобразование параметров для метода класса?

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

Интересно то же самое, и думаю, что это будет полезный синтаксис для некоторых бесплатных функций. Обычно я хочу, чтобы ссылка на производный класс неявно давала базовый класс, за исключением случаев, когда может произойти нежелательное сечение, например, с функцией swap (). Наличие подкачки (явное Foo & lhs, явное Foo & rhs) было бы утешительным.

Dwayne Robinson 30.05.2014 05:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
53
1
20 345
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Компилятор выдал предупреждение о "неоднозначном вызове", которого будет достаточно.

Я занимался разработкой TDD и не осознавал, что забыл реализовать соответствующий вызов в фиктивном объекте.

Возможно, вы захотите обновить вопрос, чтобы отразить, что указанная вами проблема (выбран неправильный метод) на самом деле не является проблемой. Я пересматривал это для своих собственных целей и понял, что даже мое решение не обязательно решает это, в зависимости от аргументов.

Patrick Johnmeyer 14.04.2009 20:27

Вы также можете написать версию int, которая вызывает bool.

Он хочет предотвратить такое поведение.

Sebastian Mach 22.11.2011 16:31

Нет. explicit предотвращает автоматическое преобразование между определенными классами независимо от контекста. И, конечно, вы не можете этого сделать для встроенных классов.

Проголосовал; в конце концов, это действительно ответ на вопрос правильно.

Patrick Johnmeyer 07.10.2008 00:30

bool ЯВЛЯЕТСЯ int, который ограничен значением 0 или 1. В этом вся концепция return 0; логически то же самое, что сказать return false; (однако не используйте это в коде).

Я считаю, что bool - это самостоятельный тип в C++.

Head Geek 07.10.2008 03:08

Да, это тип, но этот тип может содержать только целые значения 0 и 1. На самом деле, если я помню своего инструктора по C++ из моих дней коллажа, 0 - это ложь, а все остальное - истина. Это внутренняя работа bool, а не то, как ее следует применять.

WolfmanDragon 08.10.2008 00:35

Не быть придурком, а «коллажными днями»? Хе-хе-хе ... заставил меня усмехнуться.

Patrick Johnmeyer 20.10.2008 17:42

Вы правы, что bools могут содержать только значения 0 и 1; преобразовать bool в int, вы получите 0 или 1. «0 - ложь, а все остальное - истина» - применяется в обратном случае; если вы оцениваете int или другое числовое значение как bool. Идея возврата 0 фактически пришла из C, когда не было типа bool.

Patrick Johnmeyer 20.10.2008 17:44

Я все еще должен быть в одном, мне потребовалось до сегодняшнего дня, чтобы уловить свой каламбур.

WolfmanDragon 22.10.2008 02:10
Ответ принят как подходящий

Нет, вы не можете использовать явное, но вы можете использовать шаблонную функцию для обнаружения неверных типов параметров.

С помощью 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, не будет скомпилирована, потому что член является частным, конечно, с учетом стандартных исключений из правил видимости (друг, внутренние вызовы и т. д.). Если что-то, у кого есть доступ, вызывает частный метод, вы получите ошибку компоновщика.

Но это отключит ВСЕ автоматические преобразования.

Lev 07.10.2008 00:35

Да, это отключит ВСЕ автоматические преобразования. Дэн хочет «предотвратить преобразование параметров для метода класса» согласно тексту вопроса, и этот метод удовлетворяет это. Есть способы использования специализации шаблонов, с помощью которых при желании мы могли бы «сопоставить» базовые типы с конкретными методами.

Patrick Johnmeyer 07.10.2008 00:52

Я понимаю, что это старый ответ. Есть ли способ использовать delete или некоторые другие современные функции C++ для достижения того же самого без этого взлома private? Обновлено: Ахах! Оно работает!

Apollys supports Monica 20.09.2019 04:45

Да, вы можете использовать delete, хотя я бы не назвал использование private хакерским. Просто не по-новому. Это то место, где StackOverflow не всегда справляется с изменениями. Как бы вы относились к включению своего ответа в мой, @Apollys, с разделами для C++ 11 и до C++ 11?

Patrick Johnmeyer 25.09.2019 08:27

Конечно, вы бы предпочли оставить исходный ответ сверху или вариант C++ 11 сверху?

Apollys supports Monica 25.09.2019 21:44

Сделанный. Сначала я разместил решение C++ 11, потому что в наши дни оно действительно должно стать популярным вариантом.

Apollys supports Monica 26.09.2019 03:33

Что-то, что может сработать для вас, - это использовать шаблоны. Ниже показана функция шаблона 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> для явного сопоставления. Это лучший способ, если он хочет разрешить некоторые преобразования, но не все.

Patrick Johnmeyer 07.10.2008 01:23

Собственно, это действительно тот же метод, что и ваш. Когда я быстро просканировал ваш незадолго до публикации, я был сбит с толку многопараметрическими прототипами и подумал, что ваша техника делает что-то другое, чего я не полностью понимал. Надо было читать повнимательнее.

Michael Burr 07.10.2008 01:31

Ах, многопараметрические версии должны были прояснить, что это применимо не только к методам с одним параметром, и что только рассматриваемый аргумент должен быть шаблонизирован. Может быть, они больше сбивали с толку, чем приносили пользу? Я добавлю комментарий, чтобы прояснить это.

Patrick Johnmeyer 07.10.2008 07:18

То, что они немного сбили меня с толку, не означает, что они сбивали с толку. Если в этом есть смысл.

Michael Burr 07.10.2008 20:49

Ниже приводится очень простая оболочка, которую можно использовать для создания строгого определения типа:

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?

SebNag 25.01.2017 22:41

@SebTu: № Height и Width должны быть найдены без поиска ADL. Выражение Height(int_1) - это явное приведение типа, в результате которого вызывается конструктор Height. ADL здесь не применяется. Следовательно, чтобы обратиться к NS::Height или NS::Width, мы должны либо использовать директиву / объявление using, либо явно указать имена.

Richard Corden 02.02.2017 14:05

В настоящее время принятый отвечать (с использованием частной шаблонной функции) хорош, но устарел. В 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 и постоянная ссылка на принятый ответ (поскольку принятый ответ может быть изменен!).

Patrick Johnmeyer 25.09.2019 08:39

Другие вопросы по теме