существует ли лучший способ выбрать корректную перегрузку метода?

Это действительно единственный способ получить корректный адрес для функции экземпляра:

typedef CBitmap * (CDC::* SelectObjectBitmap)(CBitmap*);
SelectObjectBitmap pmf = (SelectObjectBitmap)&CDC::SelectObject;

Во-первых, нужно создать определение типа, и затем нужно использовать это, чтобы вынудить компилятор выбрать корректный перегруженный метод при взятии его адреса?

Нет ли никакой синтаксис, который является более естественным, и автономным, таким как:

SelecdtObjectBitmap pmf = &CDC::SelectObject(CBitmap*);

Я часто использую ScopeGuard в моем коде. И одно очевидное использование должно гарантировать, что любые временные объекты CDC сначала выбраны в данный DC, затем удалили в выходе объема, заставив мой код протечь свободный даже при исключительных обстоятельствах - и одновременно очистив записанный код (глупый несколько путей выхода и попытки/выгоды и так далее, чтобы попытаться обработать удаление любых выбранных объектов от данного CDC).

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

// get our client rect
CRect rcClient;
GetClientRect(rcClient);

// get the real DC we're drawing on
PAINTSTRUCT ps;
CDC * pDrawContext = BeginPaint(&ps);

// create a drawing buffer
CBitmap canvas;
canvas.CreateCompatibleBitmap(pDrawContext, rcClient.Width(), rcClient.Height());

CDC memdc;
memdc.CreateCompatibleDC(pDrawContext);

//*** HERE'S THE LINE THAT REALLY USES THE TYPEDEF WHICH i WISH TO ELIMINATE ***//
ScopeGuard guard_canvas = MakeObjGuard(memdc, (SelectObjectBitmap)&CDC::SelectObject, memdc.SelectObject(&canvas));

// copy the image to screen
pDrawContext->BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), &memdc, rcClient.left, rcClient.top, SRCCOPY);

// display updated
EndPaint(&ps);

Это всегда казалось мне глупо адом, что мне нужна к определению типа каждая перегруженная функция, из которой я хочу взять адрес.

Так... существует ли лучший путь?!

Править: На основе ответов люди предоставили, я полагаю, что у меня есть решение моей базовой потребности: т.е. иметь более естественный синтаксис для MakeGuard, который выводит корректное переопределение SelectObject для меня:

template 
ObjScopeGuardImpl1 MakeSelectObjectGuard(CDC & dc, GDIObj * pGDIObj)
{
    return ObjScopeGuardImpl1::MakeObjGuard(dc, (GDIObj*(CDC::*)(GDIObj*))&CDC::SelectObject, dc.SelectObject(pGDIObj));
}

Который заставляет мой выше кода измениться на:

ScopeGuard guard_canvas = MakeSelectObjectGuard(memdc, &canvas);

//////////////////////////////////////////////////////////

Для тех, кто мог бы здесь искать версию не-MFC того же самого:

//////////////////////////////////////////////////////////////////////////
//
// AutoSelectGDIObject
//  selects a given HGDIOBJ into a given HDC,
//  and automatically reverses the operation at scope exit
//
// AKA:
//  "Tired of tripping over the same stupid code year after year"
//
// Example 1:
//  CFont f;
//  f.CreateIndirect(&lf);
//  AutoSelectGDIObject select_font(*pDC, f);
//
// Example 2:
//  HFONT hf = ::CreateFontIndirect(&lf);
//  AutoSelectGDIObject select_font(hdc, hf);
//
// NOTE:
//  Do NOT use this with an HREGION.  Those don't need to be swapped with what's in the DC.
//////////////////////////////////////////////////////////////////////////

class AutoSelectGDIObject
{
public:
    AutoSelectGDIObject(HDC hdc, HGDIOBJ gdiobj) 
        : m_hdc(hdc)
        , m_gdiobj(gdiobj)
        , m_oldobj(::SelectObject(m_hdc, gdiobj))
    {
        ASSERT(m_oldobj != m_gdiobj);
    }

    ~AutoSelectGDIObject()
    {
        VERIFY(m_gdiobj == ::SelectObject(m_hdc, m_oldobj));
    }

private:
    const HDC       m_hdc;
    const HGDIOBJ   m_gdiobj;
    const HGDIOBJ   m_oldobj;
};

//////////////////////////////////////////////////////////

Спасибо Все, кто ответил и прокомментировал!:D

5
задан Mordachai 14 January 2010 в 17:22
поделиться

4 ответа

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

из раздела 13.4 / 1 («Адрес перегруженной функции» («Over.Over]):

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

  • объектом или ссылкой инициализирована (8,5, 8.5.3),
  • левая сторона назначения (5.17),
  • параметр функции (5.2.2),
  • Параметр определенного пользовательского оператора (13.5),
  • возвращаемого значения функции, функции оператора или преобразования (6.6.3) или
  • явного преобразования типа (5.2.3, 5.2.9 , 5.4).

Название функции перегрузки может предшествовать оператору & . Перегруженное имя функции не должно использоваться без аргументов в контекстах, отличных от перечисленных. [ Примечание: Любой резервный набор скобок, окружающих перегруженное имя функции, игнорируется (5.1). ]

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

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

2
ответ дан 14 December 2019 в 19:15
поделиться
-

Вы можете избежать использования Typedef, но это действительно не красиво:

void foo(int){
    std::cout << "int" << std::endl;
}

void foo(float){
    std::cout << "float" << std::endl;
}

int main()
{
    typedef void (*ffp)(float);

    ffp fp = (void (*)(float)) foo; // cast to a function pointer without a typedef

    ((ffp)fp)(0);
}

лучше придерживаться Typedef.

1
ответ дан 14 December 2019 в 19:15
поделиться

Вы, кажется, неправильно понимаете корня проблемы. До тех пор, пока бетонный тип на приемной стороне присвоения / инициализации, известен для компилятора, вы не должны иметь никаких усилий для «выбрать» вообще, если ваш Typedef определяется как

typedef CBitmap * (CDC::* SelectObjectBitmap)(CBitmap*); 

, а затем инициализация

SelectObjectBitmap pmf = &CDC::SelectObject; 

Требуется, чтобы вызвать разрешение перегрузки и выбирать правильную перегруженную версию функции без необходимости явного отряда. Это на самом деле только одно место в C ++ при разрешении перегрузки в правой части выражения / инициализации зависит от левой стороны.

Конечно, то же самое должно работать без типов.


Хорошо, увидев, что то, что вы на самом деле делаете, передает адрес метода к функции шаблона в качестве зависимого типа аргумента: конечно, в этом случае у вас есть либо 1), выберите правильную перегрузку с помощью активов, или 2) исправить тип параметра функции, явно указывая аргумент шаблона.

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

SelectObjectBitmap pmf = &CDC::SelectObject; 

, а затем пройдите PMF к функции шаблона.

0
ответ дан 14 December 2019 в 19:15
поделиться

Короткий ответ - «нет», невозможно избежать создания кадра данных. ggplot требует, чтобы данные находились в кадре данных. При использовании qplot можно присвоить ему отдельные векторы для x и y, но внутри него по-прежнему создается кадр данных из передаваемых параметров.

Я согласен с предложением Джубы - научись использовать функцию изменения формы или лучше, чем изменение формы с функциями плавления / литья . После быстрого размещения данных в длинном формате создание удивительных графиков ggplot становится на шаг ближе!

-121--1579046-

Это далеко не обычная практика, на самом деле это, вероятно, будет рассматриваться как плохая практика!

CSS используется для установки стилей страницы и визуализируется при загрузке страницы. Javascript вступает в игру после загрузки страницы и может манипулировать существующими стилями и страницей.

Если бы вы внедрили его во все JS, это было бы крайне неэффективно! DOM манипуляции становится дорогим, если вы делаете это много в JS, и делать все стили в javascript вместо CSS будет много манипуляции. Нагрузка на клиента была бы нелепой и наверняка было бы заметное отставание в клиенте, так как он пытался внести все эти изменения. Кроме того, что произойдет, если клиент отключил javascript? Там идет весь ваш сайт внешний вид и ощущение.

Подумайте о CSS как об определении того, как страница выглядит и должна быть визуализирована, а затем подумайте о JS как об изменении этой страницы после завершения рендеринга. Вы не должны вставлять что-либо в javascript, что вы можете сделать с простым CSS стиль спереди.

-121--4132739-

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

struct XXX {
    int member() { return 0; }
    int member(int) { return 1; }
};

int main() {     
    int (XXX::*pmfv)(void) = (int (XXX::*)())&XXX::member;
    int (XXX::*pmfi)(int) = (int (XXX::*)(int))&XXX::member;
    return 0;
}

Хотя это возможно и должно быть принято любым надлежащим образом функционирующим компилятором, я не могу сказать, что я бы действительно посоветовал это - в то время как он создает и инициализирует указатель в одной инструкции, если задействованный тип имеет длинное имя, он, вероятно, в конечном итоге будет представлять собой пару строк кода, как раз из-за длины.

Я полагаю, что C++ 0x, auto должны позволить первый пример выше быть сокращены до чего-то подобного:

auto pmf = (int (XXX::*)())&XXX::member;

Это должно сделать гораздо проще, чтобы избежать typedef (и в зависимости от компилятора вы используете, вы можете уже иметь это доступно).

2
ответ дан 14 December 2019 в 19:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: