Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

[дизайн и эволюция] провалы в variadic конструкторы - C++

Войти
Регистрация
Восстановить пароль
Другие темы раздела
C++ MPI задача коммивояжера методом ветвей и границ http://www.cyberforum.ru/cpp/thread1637290.html
помогите реализацией задача коммивояжера методом ветвей и границ на вычислительном кластере. может у кого то готовая программа есть или пример
C++ Нужна программа Логическая игра "Маджонг" может кто делал Игра Маджонг, В ходе игры необходимо очистить игровое поле, убирая одинаковые фишки. удаляться они могут только в том случае, если у фишки открыта хотя бы одна сторона. Сложность игры зависит от... http://www.cyberforum.ru/cpp/thread1637074.html
C++ Скомпилировать OpenPegasus под Win32. C/C++
Помогите скомпилировать OpenPegasus под Win32. C/C++ Есть проект OpenPegasus под Unix. Нужно его скомпилировать под Windows. Цель - поднять WMI/Wbem service provider на Win32. Исходники...
Снимок с веб-камеры C++
нашел такой пример, который выводит на окно видео в режиме онлайн с вэб камеры http://pastebin.com/c9LCaLRT (из за ограничения количества символов в сообщении пришлось залить код на пастебин) ...
C++ При расшифровке RSA-сообщений на клиенте происходит ошибка http://www.cyberforum.ru/cpp/thread1634821.html
разрабатываю приложение на основе протокола подбрасывания честной монеты с использованием открытых и закрытых ключей, конкретно - ключей RSA. Сгенерила две пары ключей RSA, как и требуется, с...
C++ Написать программу для наххождения НОД, НОК Разработка Windows-приложения (в Qt) определения наибольшего общего делителя, наименьшего общего кратного и простых чисел для заданных больших чисел. Именно чтобы можно было вводить большие числа,... подробнее

Показать сообщение отдельно
hoggy
Нарушитель
6589 / 2770 / 476
Регистрация: 15.11.2014
Сообщений: 6,122
Завершенные тесты: 1
13.01.2016, 04:36  [ТС]
Часть 3.

способ для 1 параметра шаблона отлично работает.
и это - хорошая новость.

плохая заключается в том,
что это работает только, и только для 1 параметра шаблона.
и не работает для вариадиков.

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

ситуация усугубляется ещё и тем, что их может не быть вообще.
(пустой конструктор, например)

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

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


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

немножко о конкурсе шаблоно перегрузок:
если есть шаблоно-вариадик,
и просто шаблон с фиксированным количеством параметров,
то предпочтение будет отдано второй версии при условии,
что количество параметров точно совпадает.

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

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

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

итак, знакомьтесь с новой техникой с++11:
делегирующие конструкторы:

http://rextester.com/ADMR12889

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>
#include <cassert>
 
enum eCTOR{ eNONE, eVARIADIC, eONE, eCOPY, eRVALUE };
 
 
namespace detail{
    
    // --- предназначен для решения проблемы 
    // вызовов конструкторов в ситуации, 
    // когда любые аргументы проваливаются 
    // в вариадик-конструкторе
    template<class D, class B> class selector
    {
        typedef typename std::remove_reference<
            typename std::remove_reference<D>::type
        >::type
            derrived_t;
            
        typedef typename std::remove_reference<
            typename std::remove_reference<B>::type
        >::type
            base_t;
 
        enum { 
            eIS_DERRIVED = std::is_base_of<base_t, derrived_t>::value           
        };
 
        enum { eIS_RVALUE = std::is_rvalue_reference<D>::value };
 
    public:
        enum { eCOPY   =  eIS_DERRIVED && !eIS_RVALUE };
        enum { eRVALUE =  eIS_DERRIVED &&  eIS_RVALUE };
        enum { eOTHER  = !eIS_DERRIVED                };
    };
 
    template<class D, class B>
        using for_copy 
            = typename std::enable_if< selector<D,B>::eCOPY>::type*;
 
    template<class D, class B>
        using for_rvalue 
            = typename std::enable_if< selector<D,B>::eRVALUE>::type*;
 
    template<class D, class B>
        using for_other
            = typename std::enable_if< selector<D,B>::eOTHER>::type*;
    
}//namespace detail
 
struct example
{
        #define dRVALUE detail::for_rvalue<A&&, example> = nullptr
        #define dCOPY   detail::for_copy  <A&&, example> = nullptr
        #define dANY    detail::for_other <A&&, example> = nullptr
 
        template<class... Args> 
        example(Args&&... args):m_ctor(eVARIADIC)
            { std::cout << "variadic\n"; }
 
    // --- тоже самое, что вариадик, только для 1 параметра
        template<class A, dANY> 
        example(A&& a):m_ctor(eONE)
            { std::cout << "one\n"; }
 
    // --- перенаправление на констурктор копии
        template<class A, dCOPY> 
            example(A&& rhs): example(static_cast<const example&>(rhs))
            {}
    
    // --- перенаправление на констурктор перемещения
        template<class A, dRVALUE> example(A&& rhs): 
            example(std::move(static_cast<example&&>(rhs)))
            {}
 
        example(const example&):m_ctor(eCOPY)
            { std::cout << "copy\n"; }
    
        example(example&& ):m_ctor(eRVALUE)
            { std::cout << "move\n"; }
 
        #undef dRVALUE 
        #undef dCOPY   
        #undef dANY    
 
    eCTOR m_ctor;
};
 
 
struct der: example
{
    der(const int a):example(a){}
    der(const int a, const int b):example(a,b){}
    der(const der& rhs):example(rhs){}
    der(der&& rhs):example(std::move(rhs)){}
};
 
der rvalue() {  return std::move(der(10)); }
 
int main()
{
    std::cout <<"hello, world\n";
 
    der d1(10,20);
    assert(d1.m_ctor==eVARIADIC);
    
    der d2(10);
    assert(d2.m_ctor==eONE);
 
    der d3 = d1;
    assert(d3.m_ctor==eCOPY);
    
    der d4 = rvalue();
    assert(d4.m_ctor==eRVALUE);
    
}
вот так: немножко там шаблончик, тут макросик,
и вуаля! вариадик взят под контроль.

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

лично я для подобных целей завел библиотеку
под названием "костыль" "workground"
что в переводе на русски, должно означать "обходной путь":

итоговое решение:

http://rextester.com/FUHG59360

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <iostream>
#include <cassert>
 
// =====================================================================================
// =====================================================================================
// =====================================================================================
// весь этот ужас скрыт в библиотеке "workground"
//
// workground.h
//
namespace detail{
    
    // --- предназначен для решения проблемы 
    // вызовов конструкторов в ситуации, 
    // когда любые аргументы проваливаются 
    // в вариадик-конструкторе
    template<class D, class B> class selector
    {
        typedef typename std::remove_reference<
            typename std::remove_reference<D>::type
        >::type
            derrived_t;
            
        typedef typename std::remove_reference<
            typename std::remove_reference<B>::type
        >::type
            base_t;
 
        enum { 
            eIS_DERRIVED = std::is_base_of<base_t, derrived_t>::value           
        };
        
        enum { eIS_RVALUE = std::is_rvalue_reference<D>::value };
 
    public:
        enum { eCOPY   =  eIS_DERRIVED && !eIS_RVALUE };
        enum { eRVALUE =  eIS_DERRIVED &&  eIS_RVALUE };
        enum { eOTHER  = !eIS_DERRIVED                };
    };
 
    template<class D, class B>
        using for_copy 
            = typename std::enable_if< selector<D,B>::eCOPY>::type*;
 
    template<class D, class B>
        using for_rvalue 
            = typename std::enable_if< selector<D,B>::eRVALUE>::type*;
 
    template<class D, class B>
        using for_other
            = typename std::enable_if< selector<D,B>::eOTHER>::type*;
    
    #define dFOR_CONSTRUCTOR_(situation, type_) \
        detail::for_##situation<A&&, type_> = nullptr
    
    #define dWITH_REDIRECT_CONSTRUCTORS(type_)                \
        template<class A, dFOR_CONSTRUCTOR_(copy, type_) >    \
            type_(A&& rhs)                                    \
                : type_(static_cast<const type_&>(rhs))       \
            {}                                                \
                                                              \
        template<class A, dFOR_CONSTRUCTOR_(rvalue, type_) >  \
            type_(A&& rhs)                                    \
                : type_(std::move(static_cast<type_&&>(rhs))) \
            {}                                                \
                                                              \
        template<class A, dFOR_CONSTRUCTOR_(other, type_)>    \
            type_(A&& a)
    
}//namespace detail
 
 
// =====================================================================================
// =====================================================================================
// =====================================================================================
 
// вот это все, что полагается видеть пользователю:
 
enum eCTOR{ eNONE, eVARIADIC, eONE, eCOPY, eRVALUE };
 
struct example
{
    template<class... Args> 
    example(Args&&... args):m_ctor(eVARIADIC)
        { std::cout << "variadic\n"; }
 
    dWITH_REDIRECT_CONSTRUCTORS(example):m_ctor(eONE)
        { std::cout << "one\n"; }
 
    example(const example&):m_ctor(eCOPY)
        { std::cout << "copy\n"; }
 
    example(example&& ):m_ctor(eRVALUE)
        { std::cout << "move\n"; }
 
    eCTOR m_ctor;
};
 
// =====================================================================================
// =====================================================================================
// =====================================================================================
 
// теперь, если пользователь захочет отнаследоваться,
// то ему вообще не обязательно думать о том ужассе
// что на самом деле таится в базовом классе
 
struct der: example
{
    der(const int a):example(a){}
    der(const int a, const int b):example(a,b){}
    der(const der& rhs):example(rhs){}
    der(der&& rhs):example(std::move(rhs)){}
};
 
der rvalue() {  return std::move(der(10)); }
 
int main()
{
    std::cout <<"hello, world\n";
 
    der d1(10,20);
    assert(d1.m_ctor==eVARIADIC);
    
    der d2(10);
    assert(d2.m_ctor==eONE);
 
    der d3 = d1;
    assert(d3.m_ctor==eCOPY);
    
    der d4 = rvalue();
    assert(d4.m_ctor==eRVALUE);
}
здесь ещё могут быть какие то редкие случаи,
которые данный код не учитывает.
например: const rvalue,
ну или volatile какие нибудь.

но это редкие кейсы.
я думаю нет смысла из-за них усложнять и без того
мало мало замороченное клюкало.

к тому же, архитектура selector`a специально ориентирована
на возможные расширения.

поэтому, вы всегда сможете его модернизировать,
и обыграть какой нибудь дополнительный кейс.
10
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru