Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
1

Шаблоны: определить класс в функции и вызвать его метод

03.08.2015, 23:33. Показов 1932. Ответов 24
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Здравствуйте форумчане.

Есть два класса:
C++
1
2
3
4
5
6
class WSelectionBox
{
public:
    WSelectionBox() = default;
    std::set<int> selectedIndexes();
};
C++
1
2
3
4
5
6
class WLineEdit
{
public:
    WLineEdit() = default;
    std::string valueText();
};
Как определить какой именно класс был передан в шаблон и вызвать его метод

C++
1
2
3
4
5
6
7
8
9
10
11
template<typename C>
void save()
{
    Widget* component_ = new C();
    // if WSelectionBox
    if(typeid(C) == typeid(WSelectionBox))
        std::set<int> selection = component_->selectedIndexes();
    // if WLineEdit
    if(typeid(C) == typeid(WLineEdit))
        std::string = component_->valueText();
}
Дабы не возникало такой ошибки?

Код
'class Wt::WLineEdit' has no member named 'selectedIndexes'
                 std::set<int> selection = component_->selectedIndexes();

С уважением QVO.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
03.08.2015, 23:33
Ответы с готовыми решениями:

Создать класс Triad (тройка чисел); определить метод сравнения триад. Определить производный класс Date
Создать класс Triad (тройка чисел); определить метод сравнения триад. Определить производный класс...

Как создать метод и вызвать его
Всем привет. Вопрос заключается в следующем. Только начинаю изучать C# и столкнулся со следующей...

Как вызвать метод из одного класса в другой класс
Подскажите пожалуйста как вызвать метод из другого класса: class A { public: B *b; void...

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

24
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
03.08.2015, 23:58 2
Интерфейсы в С++
1
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
04.08.2015, 00:07 3
Если C потомок Widget (естественно с виртуальными плюшками), то можете с помощью dynamic_cast привести component_ к нужному типу (WSelectionBox или WLineEdit), но че-т как-то не смотрится...
1
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
04.08.2015, 10:41  [ТС] 4
Камрады, есть еще какие-то подходы?
0
584 / 387 / 216
Регистрация: 20.01.2013
Сообщений: 1,169
04.08.2015, 10:54 5
Наверное как то так
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <typeinfo>>
 
namespace cf{
    class Widget{
        public:
            virtual void make() = 0;
    };
    
    class WLineEdit : public Widget{
        public:
            virtual void make(){
                std::cout << "LineEdit do same" << std::endl;
            }
    };
    
    class WSelectedBox : public Widget{
        public:
            virtual void make(){
                std::cout << "SelectedBox do same" << std::endl;
            }
    };
    
    template<typename C>
    void save()
    {
        Widget * component = new C();
        // if WSelectionBox
        if(typeid(C) == typeid(WSelectedBox))
           component->make();
        // if WLineEdit
        if(typeid(C) == typeid(WLineEdit))
            component->make();
    }   
}
 
int main()
{   
    cf::save<cf::WLineEdit>();
    cf::save<cf::WSelectedBox>();
    
    
    return 0;
}
0
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
04.08.2015, 11:15 6
C++
1
2
3
4
5
 if(typeid(C) == typeid(WSelectedBox))
           component->make();
        // if WLineEdit
        if(typeid(C) == typeid(WLineEdit))
            component->make();
улыбнуло
0
Игогошка!
1801 / 708 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
04.08.2015, 11:46 7
QVO, про задачу ничего и не рассказал. Что ты хочешь сделать? Можно ли менять исходные классы? Почему именно выбрал шаблоны, а не виртуальные функции? Одно знаю точно - сохранение в том стиле, в котором хочешь сделать ты (с явными проверками на тип), делать не надо.
0
1458 / 795 / 257
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
04.08.2015, 12:21 8
Лучший ответ Сообщение было отмечено QVO как решение

Решение

Как вариант:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <set>
#include <type_traits>
#include <memory>
 
class WSelectionBox
{
public:
    WSelectionBox() = default;
    std::set<int> selectedIndexes() const {return {1,2,3};}
};
 
class WLineEdit
{
public:
    WLineEdit() = default;
    std::string valueText() const {return "Hello";}
};
 
template <typename T>
void foo_impl(T t, std::true_type) // for  WSelectionBox
{
    for (const auto &i : t->selectedIndexes()) std::cout << i << " ";
    std::cout << std::endl;
}
 
template <typename T>
void foo_impl(T t, std::false_type) // for  WLineEdit
{
    std::cout << t->valueText() << std::endl;
}
 
template <typename T>
void foo()
{
    foo_impl(std::make_shared<T>(), std::is_same<T, WSelectionBox>());
}
 
int main()
{
    foo<WSelectionBox>();
    foo<WLineEdit>();
}
3
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
04.08.2015, 13:13  [ТС] 9
ct0r, в общих чертах есть модели сущностей в БД MySQL. Хочу сделать GUI редактор БД.
Интерфейсы не подходят, т.к. придется переписывать код.

Пример модели:

C++
1
2
3
4
5
6
7
class Game
{
public:
Game();
std::string name; // название
std::vector<std::string name> tags; // теги
};
Передаю модель игры на редактирование в объект редактора:

C++
1
2
editor->make_field<Game, std::string, WLineEdit>(ptr, &Game::name, std::string("Имя"));
//const Dbo::ptr<Game>& ptr
Создается GUI поле

C++
1
2
3
4
5
6
7
template<typename T, typename R, typename C>
void make_field(Dbo::ptr<T> ptr, R T::* member, const WString& title)
{
    FieldInterface* field = new Field<T, R, C>(ptr, member, title);
    fields_.push_back(field);
//std::vector<FieldInterface*> fields_;
}
Плюс в том, что я могу создать виджет с тем количеством полей, которое мне нужно.
Допустим кликнул по названию игры, вызвалось окошко содержащее только 1 эдит под название.
Нажал на кнопку и создал виджет со всеми полями для редактирования модели. (ячейку для ввода названия игры и список для выбора уже существующих тегов)

И при нажатие кнопки сохранить после редактирование данных исполняется такой код:
C++
1
2
            for(auto field : fields_)
                field->save();
С примерно таким содержимым:
Если я передаю поле модели с типом std::string (&Game::name) то в методе возникают ошибки, т.к. std::string не владеет методами clear и insert.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        void save()
        {
            if(typeid(member_) == typeid(std::vector< std::string >))
            {
                ptr_.modify()->*member_.clear(); // тут ошибка, т.к. std::string не имеет метода clear
                WSelectionBox* component = dynamic_cast<WSelectionBox*>(component_);
                std::set<int> selection = component->selectedIndexes();
 
                for (std::set<int>::iterator it = selection.begin(); it != selection.end(); ++it)
                {
                    // тут некие манипуляции с созданием моделей для тегов
                    ptr_.modify()->*member_.insert(ptr2); // тут ошибка, т.к. std::string не имеет метода Insert
                }
            } else
            {
                ptr_.modify()->*member_ = component_->valueText();
            }
        }
0
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
05.08.2015, 23:17  [ТС] 10
DiffEreD, есть подход, который даст возможность вызвать более двух имплементируемых методов?
К примеру параметры:
C++
1
2
3
4
std::string
int
std::shared_ptr<int>
std::vector< std::shared_ptr<int> >
Для каждого из них свой исполняемый метод.

С уважением QVO.
0
1458 / 795 / 257
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
05.08.2015, 23:31 11
Лучший ответ Сообщение было отмечено QVO как решение

Решение

Делать через std::enable_if

Добавлено через 7 минут
Примерно так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <set>
#include <type_traits>
#include <memory>
 
class WSelectionBox
{
public:
    WSelectionBox() = default;
    std::set<int> selectedIndexes() const {return {1,2,3};}
};
 
class WLineEdit
{
public:
    WLineEdit() = default;
    std::string valueText() const {return "Hello";}
};
 
template <typename T>
void foo_impl(std::shared_ptr<T> t, std::enable_if_t<std::is_same<T, WSelectionBox>::value>* = nullptr) // for  WSelectionBox
{
    for (const auto &i : t->selectedIndexes()) std::cout << i << " ";
    std::cout << std::endl;
}
 
template <typename T>
void foo_impl(std::shared_ptr<T> t, std::enable_if_t<std::is_same<T,WLineEdit>::value>* = nullptr) // for  WLineEdit
{
    std::cout << t->valueText() << std::endl;
}
 
template <typename T>
void foo_impl(std::shared_ptr<T> t, std::enable_if_t<std::is_same<T,int>::value>* = nullptr) // for  int
{
    std::cout << "int" << std::endl;
}
 
template <typename T>
void foo()
{
    foo_impl(std::make_shared<T>());
}
 
int main()
{
    foo<WSelectionBox>();
    foo<WLineEdit>();
    foo<int>();
}
1
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
05.08.2015, 23:55  [ТС] 12
DiffEreD, у меня вот так вышло:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        template< typename T1, typename R1 >
        struct is_collection_point : std::integral_constant< bool,
                std::is_same< Wt::Dbo::collection< Dbo::ptr<R1> >, typename std::remove_cv<T1>::type>::value > {};
 
        template< typename T1, typename R1 >
        struct is_ptr_point : std::integral_constant< bool,
                std::is_same< Wt::Dbo::ptr<R1>, typename std::remove_cv<T1>::type>::value > {};
 
        template< typename T1 >
        struct is_wstring_point : std::integral_constant< bool,
                std::is_same< Wt::WString, typename std::remove_cv<T1>::type>::value > {};
//-------------------------------
        template< typename T1, typename R1 >
        typename std::enable_if<is_collection_point<T1, R1>::value, T1>::type foo1()
        {
            std::cout << "foo1: collection\n";
        }
 
        template< typename T1, typename R1 >
        typename std::enable_if<is_ptr_point<T1, R1>::value, T1>::type foo1()
        {
            std::cout << "foo1: ptr\n";
        }
 
        template< typename T1, typename R1 >
        typename std::enable_if<is_wstring_point<T1>::value, T1>::type foo1()
        {
            std::cout << "foo1: wstring\n";
        }
//-------------------------------
Добавлено через 3 минуты
DiffEreD, может подскажешь еще как в метапрограммирование можно задать поле, которое может быть пустым.
Сейчас на примере покажу:
C++
1
2
template< typename T, typename R, typename A >
void foo() {}
Как указать, что при вызове функции foo<T, R, A>() параметр A можно не передавать?
0
1458 / 795 / 257
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
06.08.2015, 00:13 13
Задать параметр шаблона по умолчанию:
C++
1
2
3
4
5
6
7
template <typename T, typename R, typename A = void>
void foo() {}
 
int main()
{
    foo<int, double>();
}
Добавлено через 44 секунды
Или тут что то другое имелось ввиду?
1
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
06.08.2015, 00:22  [ТС] 14
DiffEreD, подправил твои функции:

C++
1
2
3
4
5
6
7
8
9
10
11
        template <typename T1, typename R1 >
        void foo_impl(std::enable_if<std::is_same< T1, Wt::Dbo::ptr<R1> >::value>* = nullptr)
        {
            std::cout << "Wt::Dbo::ptr<R1>" << std::endl;
        }
 
        template <typename T1, typename R1 >
        void foo_impl(std::enable_if<std::is_same< T1, Wt::WString >::value>* = nullptr)
        {
            std::cout << "Wt::WString" << std::endl;
        }
C++
1
foo_impl<WString, WString>();
C++
1
foo_impl<Wt::Dbo::ptr<Game>, Game>();
Выдает ошибку компиляции:
Код
call of overloaded 'foo_impl()' is ambiguous
             foo_impl<R, ptrType>();
Где я допустил ошибку?
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
06.08.2015, 00:23 15
Лучший ответ Сообщение было отмечено QVO как решение

Решение

Цитата Сообщение от QVO Посмотреть сообщение
Как определить какой именно класс был передан в шаблон и вызвать его метод
ответ на ваш вопрос:

1.
Специализация шаблона
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
 
#include<string>
#include<set>
 
class WSelectionBox
{
public:
    WSelectionBox() = default;
    std::set<int> selectedIndexes(){ return std::set<int>();  }
};
 
class WLineEdit
{
public:
    WLineEdit() = default;
    std::string valueText() { return ""; }
};
 
 
template<typename C>
void save();
 
template<>
void save<WSelectionBox>()
{
    auto* component_ = new WSelectionBox();
    std::set<int> selection = component_->selectedIndexes();
}
 
 
template<>
void save<WLineEdit>()
{
    auto* component_ = new WLineEdit();
    std::string s = component_->valueText();
}
 
 
 
int main()
{
    std::cout << "Hello, world!\n";
    
    save<WLineEdit>();
}


2.
SFINAE - перегрузка.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
 
#include<string>
#include<set>
 
class WSelectionBox
{
public:
    WSelectionBox() = default;
    std::set<int> selectedIndexes(){ return std::set<int>();  }
};
 
class WLineEdit
{
public:
    WLineEdit() = default;
    std::string valueText() { return ""; }
};
 
 
template<typename C, 
typename std::enable_if< std::is_same<C, WSelectionBox>::value >::type* = nullptr
>
void save()
{
    auto* component_ = new C();
    std::set<int> selection = component_->selectedIndexes();
}
 
template<typename C, 
typename std::enable_if< std::is_same<C, WLineEdit>::value >::type* = nullptr
>
void save()
{
    auto* component_ = new C();
    std::string s = component_->valueText();
}
 
 
 
int main()
{
    std::cout << "Hello, world!\n";
    
    save<WLineEdit>();
}
можно в статике изнутри функции пропасти тип данных,
и делегировать в "зависимости от" каким то другим функциям.

рантайм-конструкции вида :
C++
1
 if(typeid(C) == typeid(WSelectionBox))
точно не нужны.
2
1458 / 795 / 257
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
06.08.2015, 00:26 16
QVO, а если typename добавить:
C++
1
void foo_impl(typename std::enable_if<std::is_same< T1, Wt::Dbo::ptr<R1> >::value>::type* = nullptr)
1
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
06.08.2015, 00:47  [ТС] 17
DiffEreD,
C++
1
2
3
4
5
6
7
8
9
10
11
        template <typename T1, typename R1 >
        void foo_impl(typename std::enable_if<std::is_same< T1, Wt::Dbo::ptr<R1> >::value>* = nullptr)
        {
            std::cout << "Wt::Dbo::ptr<R1>" << std::endl;
        }
 
        template <typename T1, typename R1 >
        void foo_impl(typename std::enable_if<std::is_same< T1, Wt::WString >::value>* = nullptr)
        {
            std::cout << "Wt::WString" << std::endl;
        }
Эта же ошибка:
C#
1
2
call of overloaded 'foo_impl()' is ambiguous
             foo_impl<R, ptrType>();
Добавлено через 20 минут
hoggy, можно сделать так?

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        template <typename C1,
                  typename C2,
                  typename std::enable_if< std::is_same<C1, Wt::Dbo::collection< Dbo::ptr<C2> > >::value >::type* = nullptr >
        void foo_impl()
        {
            std::cout << "Wt::Dbo::collection< Dbo::ptr<C2> >" << std::endl;
        }
 
        template < typename C1,
                   typename C2,
                   typename std::enable_if< std::is_same<C1, Dbo::ptr<C2> >::value >::type* = nullptr >
        void foo_impl()
        {
            std::cout << "Wt::Dbo::ptr<R1>" << std::endl;
        }
 
        template < typename C1,
                   typename C2 >
        void foo_impl()
        {
            std::cout << "Other type" << std::endl;
        }
Этот код не компилируется из-за последней функции.
Ошибка при компиляции:
Код
call of overloaded 'foo_impl()' is ambiguous
             foo_impl<R, ptrType>();
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
06.08.2015, 02:52 18
Цитата Сообщение от QVO Посмотреть сообщение
можно сделать так?
Цитата Сообщение от QVO Посмотреть сообщение
Этот код не компилируется из-за последней функции.
очевидно жеж, что нельзя.

итак, справочка:

как работает SFINAE ?


итак, предположим, у нас есть функция foo, которая принимает некое T.
если T - дробное число (float/double), мы хотим обработать его одним способом.
если T - интегральное число - другим способом.
во всех остальных случаях, мы хотим ошибку компиляции.

как это сделать?
можно было бы написать 100500 перегрузок под все возможные интегральные типы и плавающие.
но это - много много копипасты.
а хочется написать парочку шаблонов под два сценария.

напишем пробный шаблон:

C++
1
2
3
4
5
template<typename T> 
void foo(T&& value)
{
    std::cout<<"value = "<< value <<'\n';
}
данный шаблон способен принять любой тип,
если только он умеет выводиться в std::cout.

но он не умеет различать интегральные,дробные,и прочие.

что же делать?

как различить типы по интересным нам свойствам?

можно например, применить элементы метапрограммирования:

C++
1
2
3
4
5
6
7
8
9
10
template<typename T> 
void foo(T&& value)
{
    if( std::is_integral<T>::value )
        std::cout<<"value = "<< value << "is integral type\n";
    else if( std::is_floating_point<T>::value )
        std::cout<<"value = "<< value << "is floating type\n";
    else
        std::cout<<"value = is other\n";
}
метафункция
C++
1
std::is_integral<T>::value
даст 1, если T интегральное.
метафункция
C++
1
std::is_floating_point<T>::value
даст 1, если T дробное.
ну а иначе - это что-то другое.

у этого способа есть недостатки.

1.
ветвление логики происходит в рантайме.
то есть это не эффективно. программа будет тратить время на такую выборку.

2.
мы не получаем ошибку компиляции если T ни интегральное, ни дробное.

3.
это уже фатальный недостаток.
в зависимости от Т отработает только одна какая то ветвь исполнения.
однако в компиляцию войдут все 3 ветви.

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

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

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

итак, знакомьтесь, эту технику SFINAE официально узаконил стандарт:

C++
1
2
typedef  typename std::enable_if<BoolValue, U>::type
    result;
если BoolValue - true, тогда type будет равно U,
а значит result будет равно U


если BoolValue - false, тогда type вообще не будет существовать,
и вся эта конструкция будет не валидна (не скомпилируется)

C++
1
2
3
4
5
6
// result равно int
typedef  typename std::enable_if<1, int>::type
    result;
// не скомлируется, type не существует
typedef  typename std::enable_if<0, int>::type
    result;
очень важно понимать, что по правилу SFINAE,
если теоретически может существовать такое T,
при котором шаблоно-перегруженная функция может корректно скомпилироваться,
то компилятор не будет генерировать ошибку.

он просто отбросит такую перегрузку из конкурса выбора претендентов.
и будет пытаться использовать другую перегрузку.

теперь пишем наши сценарии обработки данных в зависимости от T:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
 
//--- вспомогательная структура 
// инкапсулирует расчеты 
// связанные с определнием свойств типа
template<typename T>struct property
{
    enum { is_integral = std::is_integral<T>::value       };
    enum { is_floating = std::is_floating_point<T>::value };
    enum { is_other = !is_integral && !is_floating        };
};
 
// --- инстанцируется только если T интегральное, иначе не существует
// а значит для T, которое не интегральное, 
// данная перегрузка просто не будет скомпилирована
template<typename T, 
    typename std::enable_if< property<T>::is_integral>::type* = nullptr  
> 
void foo(T&& value)
{
    std::cout<<"value = "<< value << " is integral type\n";
}
 
// --- инстанцируется только если T дробное, иначе не существует
// а значит для T, которое не дробное, 
// данная перегрузка просто не будет скомпилирована
template<typename T, 
    typename std::enable_if< property<T>::is_floating >::type* = nullptr  
> 
void foo(T&& value)
{
    std::cout<<"value = "<< value << " is floating type\n";
}
 
// --- инстанцируется только если T ни интегральное, и не дробное
// а значит для T, которое дробное, или интегральное
// данная перегрузка просто не будет скомпилирована
template<typename T, 
    typename std::enable_if< property<T>::is_other >::type* = nullptr  
> 
void foo(T&& value)
{
    // расскоментируйте, что бы получить ошибку компиляции
    //static_assert(!property<T>::is_other, "ERROR: T MUST BE INTERGAL OR FLOATING");
        
    std::cout<<"value is other type\n";
}
 
int main()
{
    std::cout << "Hello, world!\n";
    
    foo(10);
    foo(10.5);
    
    foo(nullptr);
}
обратите внимание на конструкцию:

C++
1
2
3
template<typename T, 
    typename std::enable_if< property<T>::is_integral>::type* = nullptr  
>
здесь первый параметр шаблона T - это аргумент, который принимает функция.
а второй пользователю указывать не нужно.
он выведется автоматически в зависимости от T

и если T подходящее - получится T*,
а если не подходящее - шаблон просто отбрасывается.

в итоге, в зависимости от Т только одна из перегрузок будет инстанцированна.
все остальные будут забракованы.

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

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

http://rextester.com/BLBO36917

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//Title of this code
//g++  4.9.1
 
#include <iostream>
 
//--- вспомогательная структура 
// инкапсулирует расчеты 
// связанные с определнием свойств типа
template<typename T>struct property
{
    enum { is_integral = std::is_integral<T>::value       };
    enum { is_floating = std::is_floating_point<T>::value };
    enum { is_other = !is_integral && !is_floating        };
};
 
//--- макросы служат для наглядности
#define dFOR_INTEGRAL\
    typename std::enable_if< property<T>::is_integral>::type* = nullptr  
#define dFOR_FLOATING\
    typename std::enable_if< property<T>::is_floating>::type* = nullptr  
#define dFOR_OTHER\
    typename std::enable_if< property<T>::is_other>::type* = nullptr  
 
//--- не нужно вникать в то, что скрывается под макросами
// просто имейте ввиду, что данная функция только для интегральных
template<typename T, dFOR_INTEGRAL > 
void foo(T&& value)
    { std::cout<<"value = "<< value << " is integral type\n"; }
 
// а эта - для дробных
template<typename T, dFOR_FLOATING> 
void foo(T&& value)
    { std::cout<<"value = "<< value << " is floating type\n"; }
 
// а эта - для всех остальных
template<typename T, dFOR_OTHER> 
void foo(T&& value)
{
    // расскоментируйте, что бы получить ошибку компиляции
    //static_assert(!property<T>::is_other, "ERROR: T MUST BE INTERGAL OR FLOATING");
        
    std::cout<<"value is other type\n";
}
 
// а не забудем удалить дефайны, они нам больше не нужны
#undef dFOR_INTEGRAL
#undef dFOR_FLOATING
#undef dFOR_OTHER
 
int main()
{
    std::cout << "Hello, world!\n";
    
    foo(10);
    foo(10.5);
    
    foo(nullptr);
}
----------------------------------------------------

Теперь возвращаемся к вашему примеру:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template <typename C1,
                  typename C2,
                  typename std::enable_if< std::is_same<C1, Wt::Dbo::collection< Dbo::ptr<C2> > >::value >::type* = nullptr >
        void foo_impl()
        {
            std::cout << "Wt::Dbo::collection< Dbo::ptr<C2> >" << std::endl;
        }
 
        template < typename C1,
                   typename C2,
                   typename std::enable_if< std::is_same<C1, Dbo::ptr<C2> >::value >::type* = nullptr >
        void foo_impl()
        {
            std::cout << "Wt::Dbo::ptr<R1>" << std::endl;
        }
 
 
      //-------------------------------------------------------------------
        template < typename C1,  //<--- смотрим сюда
                   typename C2 >
        void foo_impl()
        {
            std::cout << "Other type" << std::endl;
        }
первые две перегрузки по правилу SFINAE пройдут отбраковку.
допустим, выживет только одна.

а вот самая последняя перегрузка :

C++
1
2
3
4
5
6
template < typename C1,  //<--- смотрим сюда
                   typename C2 >
        void foo_impl()
        {
            std::cout << "Other type" << std::endl;
        }
никогда не будет забракована.
ведь SFINAE её никак не срезает.
а значит она всегда будет среди претендентов на выбор.

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

вот так у вас и образуется "неоднозначность вызова".
4
652 / 462 / 80
Регистрация: 26.10.2010
Сообщений: 1,263
Записей в блоге: 4
06.08.2015, 03:17  [ТС] 19
hoggy, благодарю за развернутый ответ!
Я разбил функции по типа и они содержат разную логику:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template < typename Member,
           typename Model,
           typename std::enable_if< std::is_same< Member, std::string >::value >::type* = nullptr >
void modify_impl()
{
    std::cout << "std::string" << std::endl;
}
 
template < typename Member,
           typename Model,
           typename std::enable_if<std::is_floating_point<Member>::value, Member>::type* = nullptr >
void modify_impl()
{
    std::cout << "floating" << std::endl;
}
 
template < typename Member,
           typename Model,
           typename std::enable_if< std::is_same< Member, int >::value >::type* = nullptr >
void modify_impl()
{
    std::cout << "int" << std::endl;
}
Функции (точнее методы) являются членами класса
И вызываются с метода save()

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template< typename DboModel, typename FieldType, typename Component, typename FieldTypeDboModel = void>
class Field : public FieldInterface
{
public:
    Field(Dbo::ptr<DboModel> ptr, FieldType DboModel::* member)
        , ptr_(ptr)
        , member_(member)
    {
        component_ = new Component();
    }
 
    void save()
    {
        modify_impl<member_, DboModel>();
    }
private:
    Dbo::ptr<DboModel> ptr_;
    FieldType DboModel::* member_;
};
Возникают такие ошибки:
Код
use of 'this' in a constant expression
             modify_impl<member_, DboModel>();
                         ^
no matching function for call to 'G::Field<G::Game, Wt::Dbo::ptr<G::Genre>, Wt::WComboBox, G::Genre>::modify_impl()'
             modify_impl<member_, DboModel>();
                        ^
no matching function for call to 'G::Field<G::Game, Wt::WString, Wt::WTextArea, void>::modify_impl()'
             modify_impl<member_, DboModel>();
                        ^
Можете подсказать где я снова ошибся?
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
06.08.2015, 03:22 20
Цитата Сообщение от QVO Посмотреть сообщение
ожете подсказать где я снова ошибся?
смотрите сюда:

C++
1
2
3
4
void save()
{
        modify_impl<member_, DboModel>(); //<--- чем является member_ ?
}
параметрами шаблона могут быть либо типы,
либо константы времени компиляции.
1
06.08.2015, 03:22
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
06.08.2015, 03:22
Помогаю со студенческими работами здесь

Передать метод как параметр и вызвать его
Доброго времени суток. Делаю XNA проект. Создаю игровое меню с кнопками. Меню - это класс, в...

Абстрактный класс и его наследники - не получается вызвать методы
Есть абстрактный класс, есть классы-наследники, реализующие методы абстрактного класса. В одном...

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

Eсть событие в WindowsForms button1_click и есть класс. Как вызвать метод класса в событии?
необходимо в самом событии баттон клик вызвать то что содержится в отдельном классе


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru